diff --git a/.actrc b/.actrc deleted file mode 100644 index 55d4656..0000000 --- a/.actrc +++ /dev/null @@ -1,11 +0,0 @@ -# Runner image configuration - use maintained catthehacker images --P ubuntu-latest=catthehacker/ubuntu:act-22.04 - -# Container architecture (explicit for M1/M2 Macs) ---container-architecture=linux/amd64 - -# Container daemon socket (will be overridden by Makefile) ---container-daemon-socket=- - -# Verbose output by default ---verbose diff --git a/.github/workflows/ai_pr_review.yml b/.github/workflows/ai_pr_review.yml new file mode 100644 index 0000000..3c6b639 --- /dev/null +++ b/.github/workflows/ai_pr_review.yml @@ -0,0 +1,25 @@ +name: Claude PR scan (Draft->Ready + Push) + +on: + pull_request: + types: [ready_for_review, synchronize, reopened] + +concurrency: + group: claude-pr-scan-${{ github.repository }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + review: + if: ${{ github.event.pull_request.draft == false }} + uses: safurrier/python-collab-template/.github/workflows/claude_pr_agent.yml@v1 + with: + skill: "codex-code-review" + secrets: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + # Optional: run a local skill from the target repo instead: + # with: + # skill: ".claude/skills/my-custom-review" + # Optional: use a raw prompt instead of a skill file: + # with: + # prompt: | + # Review this diff for security issues only. diff --git a/.github/workflows/ci-debug.yml b/.github/workflows/ci-debug.yml deleted file mode 100644 index 633dfb6..0000000 --- a/.github/workflows/ci-debug.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: CI Debug (Local Testing) - -# This workflow is designed for fast local iteration with act -# It runs a subset of checks for quick feedback -# -# Usage: make ci-debug -# -# Customize the steps below for the specific tests you're working on - -on: - workflow_dispatch: - push: - branches: - - '**' - -jobs: - quick-check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.12' - - - name: Install uv - run: pip install uv - - - name: Install dependencies - run: | - uv venv - uv sync --all-extras - - # Customize these steps based on what you're debugging - - name: Run Linter - run: uv run -m ruff check src - - - name: Run Type Checker - run: uv run ty check src - - # Add more steps as needed for debugging - # - name: Run Specific Test - # run: uv run -m pytest tests/test_specific.py -v diff --git a/.github/workflows/claude_pr_agent.yml b/.github/workflows/claude_pr_agent.yml new file mode 100644 index 0000000..52069c3 --- /dev/null +++ b/.github/workflows/claude_pr_agent.yml @@ -0,0 +1,112 @@ +name: Claude PR agent (skill runner) + +on: + workflow_call: + inputs: + skill: + description: | + Skill to run. Either: + - A bare name resolved from this repo's skills/ directory: + "codex-code-review" → skills/codex-code-review/SKILL.md + "context-files" → skills/context-files/SKILL.md + - A target-repo-relative path to a skill directory or file: + ".claude/skills/my-skill" → .claude/skills/my-skill/SKILL.md + "./skills/my-skill" → skills/my-skill/SKILL.md + "./skills/my-skill.md" → skills/my-skill.md (flat prompt) + Rule: values containing "/" are resolved from the target repo checkout; + bare names are resolved from this central workflow repo. + type: string + required: false + default: "" + args: + description: | + Substituted for $ARGUMENTS in the skill body. + Example: "auto ." for the context-files skill. + type: string + required: false + default: "" + prompt: + description: | + Raw prompt text used as-is. Alternative to `skill` for quick + one-off instructions without a skill file. + type: string + required: false + default: "" + secrets: + ANTHROPIC_API_KEY: + required: true + +jobs: + run-skill: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + issues: write + + steps: + # Checkout the *target repository* so Claude can read the codebase. + - name: Checkout target repo + uses: actions/checkout@v4 + + # Checkout this workflow repo to access the skills/ directory. + - name: Checkout workflow repo (for skills) + uses: actions/checkout@v4 + with: + repository: safurrier/python-collab-template + path: _ai_workflows + + - name: Load skill + id: skill + shell: bash + run: | + SKILL="${{ inputs.skill }}" + ARGS="${{ inputs.args }}" + PROMPT="${{ inputs.prompt }}" + + write_output() { + echo "text<> "$GITHUB_OUTPUT" + printf "%s\n" "$1" >> "$GITHUB_OUTPUT" + echo "EOF" >> "$GITHUB_OUTPUT" + } + + # Raw prompt mode — use as-is + if [ -z "$SKILL" ] && [ -n "$PROMPT" ]; then + write_output "$PROMPT" + exit 0 + fi + + if [ -z "$SKILL" ]; then + echo "Error: either 'skill' or 'prompt' input must be provided." >&2 + exit 1 + fi + + # Resolve skill file path + if [[ "$SKILL" == */* ]]; then + # Contains a slash — path in the target repo + if [ -d "$SKILL" ]; then + skill_file="${SKILL}/SKILL.md" # directory-style skill + else + skill_file="$SKILL" # direct .md file + fi + else + # Bare name — central repo skill + skill_file="_ai_workflows/skills/${SKILL}/SKILL.md" + fi + + if [ ! -f "$skill_file" ]; then + echo "Error: skill file not found: $skill_file" >&2 + exit 1 + fi + + # Yank (strip) YAML frontmatter, then substitute $ARGUMENTS + body=$(awk 'BEGIN{fm=0} /^---$/{fm++; next} fm<2{next} {print}' "$skill_file") + body="${body//\$ARGUMENTS/$ARGS}" + + write_output "$body" + + - name: Run Claude + uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + prompt: ${{ steps.skill.outputs.text }} diff --git a/.github/workflows/context_files_pr.yml b/.github/workflows/context_files_pr.yml new file mode 100644 index 0000000..27195b2 --- /dev/null +++ b/.github/workflows/context_files_pr.yml @@ -0,0 +1,26 @@ +name: Context files agent (PR opened / Draft->Ready) + +on: + pull_request: + types: [opened, ready_for_review] + +concurrency: + group: context-files-agent-${{ github.repository }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + context-files: + if: ${{ github.event.pull_request.draft == false }} + uses: safurrier/python-collab-template/.github/workflows/claude_pr_agent.yml@v1 + with: + skill: "context-files" + args: "auto ." + secrets: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + # Optional: scope to a subdirectory: + # with: + # skill: "context-files" + # args: "auto src/" + # Optional: use a local skill from the target repo: + # with: + # skill: ".claude/skills/context-files" diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 2668105..f8f71a2 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,82 +1,38 @@ -name: Deploy Documentation +name: Documentation on: push: - branches: - - main + branches: [main] paths: - - 'docs/**' - - 'mkdocs.yml' - - '.github/workflows/docs.yml' + - "docs/**" + - "mkdocs.yml" pull_request: - branches: - - main + branches: [main] paths: - - 'docs/**' - - 'mkdocs.yml' - -permissions: - contents: write + - "docs/**" + - "mkdocs.yml" jobs: deploy: - runs-on: ubuntu-latest if: github.event_name == 'push' - + runs-on: ubuntu-latest + permissions: + contents: write steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Configure Git Credentials - run: | - git config user.name github-actions[bot] - git config user.email 41898282+github-actions[bot]@users.noreply.github.com - - uses: actions/setup-python@v5 with: - python-version: 3.x - - - name: Install uv - run: pip install uv - - - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV - - - uses: actions/cache@v4 - with: - key: mkdocs-material-${{ env.cache_id }} - path: .cache - restore-keys: | - mkdocs-material- - - - name: Install dependencies - run: uv sync --group dev - - - name: Deploy documentation - run: uv run mkdocs gh-deploy --force + python-version: "3.12" + - run: pip install mkdocs-material + - run: mkdocs gh-deploy --force build-check: - runs-on: ubuntu-latest if: github.event_name == 'pull_request' - + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 with: - python-version: 3.x - - - name: Install uv - run: pip install uv - - - name: Install dependencies - run: uv sync --group dev - - - name: Build documentation - run: uv run mkdocs build --strict - - - name: Check build output - run: | - echo "✅ Documentation builds successfully" - echo "📊 Site size: $(du -sh site/ | cut -f1)" - echo "📄 Pages built: $(find site/ -name "*.html" | wc -l)" \ No newline at end of file + python-version: "3.12" + - run: pip install mkdocs-material + - run: mkdocs build --strict diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index 8df2d81..0000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Code Quality Checks - -on: - pull_request: - -jobs: - checks: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.12' - - - name: Install uv - run: pip install uv - - - name: Install dependencies - run: | - uv venv - uv sync --all-extras - - - name: Determine module name - id: module - run: | - if [ -d "src" ]; then - echo "name=src" >> $GITHUB_OUTPUT - else - MODULE_NAME=$(basename $(find . -maxdepth 1 -type d -not -path "*/\.*" -not -path "./tests" -not -path "./scripts" -not -path "./docker" -not -path "." | sort | head -1)) - echo "name=$MODULE_NAME" >> $GITHUB_OUTPUT - fi - - - name: Run Linter - run: uv run -m ruff check --fix ${{ steps.module.outputs.name }} - - - name: Run Formatter - run: uv run -m ruff format ${{ steps.module.outputs.name }} - - - name: Run Tests - run: uv run -m pytest tests --cov=${{ steps.module.outputs.name }} --cov-report=term-missing --cov-report=xml - - - name: Run ty - run: uv run ty check ${{ steps.module.outputs.name }} - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 - continue-on-error: true - with: - files: coverage.xml - fail_ci_if_error: false diff --git a/.gitignore b/.gitignore index ff26fa8..780bfaf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,30 +1,13 @@ .DS_Store .idea/ -.venv/ -_build/ -notebooks/ -setup -conf.py -index.rst -.coverage -*.pyc -setup_mac.sh -docker/.env .vs_code/* -.mypy_cache/* -.pytest_cache/* -.ruff_cache/* .aider* -CLAUDE.md -# Documentation build artifacts -site/ -.cache/ - -# AI/planning artifacts -.ai/** - -# act (GitHub Actions local runner) +# GitHub Actions local runner (act) +.actrc .actrc.local .secrets .env.act + +# AI/planning artifacts +.ai/** diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index c949a29..0000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,27 +0,0 @@ -repos: -- repo: local - hooks: - - id: lint - name: Run Linter - entry: make lint - language: system - always_run: true - pass_filenames: false - - id: format - name: Run Formatter - entry: make format - language: system - always_run: true - pass_filenames: false - - id: test - name: Run Tests - entry: make test - language: system - always_run: true - pass_filenames: false - - id: ty - name: Run ty - entry: make ty - language: system - always_run: true - pass_filenames: false diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..d2df28c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,110 @@ +# AGENTS.md + +## WHY — What this repo is + +Central GitHub Actions workflow host for **Claude PR agents** — reusable, prompt-driven automation that attaches to pull requests. Instead of duplicating workflow logic across repos, any target repo references this one with a single `uses:` line and gets automated code review, context file generation, or any other Claude-powered task. + +The repo is intentionally minimal: workflow YAML, prompt files, and documentation. No build system, no runtime dependencies. + +--- + +## WHAT — Repo map + +``` +.github/workflows/ + claude_pr_agent.yml # Reusable: generic skill runner (workflow_call) + ai_pr_review.yml # Caller example: code review for this repo + context_files_pr.yml # Caller example: context files agent for this repo + docs.yml # MkDocs build + GitHub Pages deploy + +skills/ + codex-code-review/ + SKILL.md # Codex Code Review prompt (verbatim body + frontmatter) + context-files/ + SKILL.md # Context files generator/updater prompt (with $ARGUMENTS) + +docs/ # MkDocs documentation site source + index.md # Overview and architecture + user-stories.md # Who uses this and why + using.md # Step-by-step setup for target repos + contributing.md # How to add a new agent + agents/ + index.md # Agent catalog + pr-review.md # Code review skill docs + context-files.md # Context files skill docs +``` + +--- + +## HOW — How to work here + +### Adding a new agent + +1. Create `skills//SKILL.md` — YAML frontmatter + prompt body (frontmatter is stripped before Claude sees it) +2. Create `docs/agents/.md` — trigger table, caller snippet, customization options +3. Add entry to `docs/agents/index.md` and `mkdocs.yml` nav +4. Tag a new release: `git tag vX.Y && git push origin vX.Y` + +No new workflow file needed — `claude_pr_agent.yml` handles all skills generically. + +See `docs/contributing.md` for the full checklist. + +### Updating a skill + +Edit `skills//SKILL.md`. Bump the version tag so target repos can opt in to the updated skill on their own schedule. + +### Working on docs + +```bash +# Preview locally (requires mkdocs-material) +pip install mkdocs-material +mkdocs serve +``` +⏸️ Not executed — requires network install. Confirm `mkdocs.yml` exists: ✅ + +```bash +# Strict build (catches broken links, missing pages) +mkdocs build --strict +``` +⏸️ Not executed — same prerequisite. + +### Validating workflow YAML + +```bash +yamllint .github/workflows/ +``` +⏸️ Not executed — `yamllint` not confirmed in this environment. Workflows are validated by GitHub Actions on push. + +--- + +## Common commands (validated) + +| Command | Status | Notes | +|---|---|---| +| `mkdocs serve` | ⏸️ not executed | Requires `pip install mkdocs-material`; run from repo root | +| `mkdocs build --strict` | ⏸️ not executed | Same prerequisite; used in CI (`docs.yml`) | +| `git tag vX.Y && git push origin vX.Y` | ⏸️ not executed | Release process; target repos pin to these tags | + +No test suite, linter, or build step for the workflows themselves — validation happens when GitHub Actions parses them on push. + +--- + +## Progressive disclosure + +- `docs/using.md` — full setup walkthrough for target repos +- `docs/contributing.md` — step-by-step skill contribution guide +- `docs/agents/pr-review.md` — review skill: triggers, local skill override, sequencing +- `docs/agents/context-files.md` — context files skill: modes, `args`, local override +- `.github/workflows/claude_pr_agent.yml` — canonical source for the generic skill runner +- `skills/context-files/SKILL.md` — canonical source for context files skill spec + +--- + +## Gotchas + +- **Repo rename breaks callers**: `claude_pr_agent.yml` hard-codes `repository: safurrier/python-collab-template` to check out the skills. If this repo is renamed, update that field. +- **Private repo access**: if this repo is private, target repos must be granted access via Settings → Actions → Access → "Accessible from repositories in your account". +- **Tag before using**: target repos reference `@v1` (or another tag). Push a tag before pointing any repo at this one. +- **`contents: write` always on**: the generic workflow always requests `contents: write` so any skill can commit files. This is intentional. +- **`$ARGUMENTS` substitution**: skills use a `$ARGUMENTS` placeholder that the workflow substitutes via `sed` at runtime using the `args:` input. It is not a shell variable — do not use `${ARGUMENTS}` or `$ARGS`. +- **Frontmatter stripping**: the `awk` command in the workflow strips everything between the first and second `---` blocks. The skill body starts on the line after the closing `---`. diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 2569156..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,40 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -### Added -- Integration with uv for dependency management -- Modern Python development tools: - - ruff for linting and formatting - - ty for type checking - - pytest with coverage reporting -- GitHub Actions workflow for automated testing -- Docker development environment improvements -- Local CI testing with act for running GitHub Actions workflows locally -- Fast debug workflow for iterative development -- Make targets: `act-install`, `ci-list`, `ci-local`, `ci-local-docs`, `ci-debug`, `ci-clean` - -### Changed -- Switched from pip/venv to uv for environment management -- Updated example code to pass ty type checking -- Modernized project structure and development workflow -- Updated Python version to 3.12 - -### Removed -- Legacy dependency management approach -- Outdated Docker configuration elements - -### Fixed -- Type hints in example code to pass ty checks -- Docker environment management -- Development workflow and quality checks - -## [0.1.0] - 2024-04-14 -- Initial fork from eugeneyan/python-collab-template -- Added Docker environment management -- Setup package installation configuration diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 9adc137..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,41 +0,0 @@ -# Contributing to Python Collab Template - -Thank you for your interest in contributing to this project! - -## Getting Started - -1. Fork the repository -2. Clone your fork: `git clone git@github.com:your-username/python-collab-template.git` -3. Create a new branch: `git checkout -b feature-name` -4. Make your changes -5. Run quality checks: `make check` -6. Commit your changes: `git commit -m "Description of changes"` -7. Push to your fork: `git push origin feature-name` -8. Open a Pull Request - -## Development Setup - -```bash -# Install dependencies and set up environment -make setup - -# Run all quality checks -make check -``` - -## Code Quality Standards - -- All code must be typed with proper type hints -- Tests must be included for new features -- Documentation must be updated when necessary -- All quality checks must pass (`make check`) - -## Pull Request Process - -1. Update the README.md with details of significant changes -2. Update the CHANGELOG.md following the existing format -3. The PR will be merged once you have the sign-off of at least one maintainer - -## Questions? - -Feel free to open an issue for any questions or concerns. diff --git a/Makefile b/Makefile deleted file mode 100644 index 9446886..0000000 --- a/Makefile +++ /dev/null @@ -1,310 +0,0 @@ -.PHONY: compile-deps setup clean-pyc clean-test clean-venv clean test ty lint format check clean-example docs-install docs-build docs-serve docs-check docs-clean dev-env refresh-containers rebuild-images build-image push-image - -# Module name - will be updated by init script -MODULE_NAME := src - -# Development Setup -################# -compile-deps: # Compile dependencies from pyproject.toml - uv pip compile pyproject.toml -o requirements.txt - uv pip compile pyproject.toml --extra dev -o requirements-dev.txt - -PYTHON_VERSION ?= 3.12 - -ensure-uv: # Install uv if not present - @which uv > /dev/null || (curl -LsSf https://astral.sh/uv/install.sh | sh) - -setup: ensure-uv ensure-scripts # Install dependencies - UV_PYTHON_VERSION=$(PYTHON_VERSION) uv sync --all-extras - $(MAKE) install-hooks - -install-hooks: # Install pre-commit hooks if in a git repo with hooks configured - @if [ -d .git ] && [ -f .pre-commit-config.yaml ]; then \ - echo "Installing pre-commit hooks..."; \ - uv run pre-commit install; \ - fi - -ensure-scripts: # Ensure scripts directory exists and files are executable - mkdir -p scripts - chmod +x scripts/*.py - -# Cleaning -######### -clean-pyc: # Remove Python compilation artifacts - find . -name '*.pyc' -exec rm -f {} + - find . -name '*.pyo' -exec rm -f {} + - find . -name '*~' -exec rm -f {} + - find . -name '__pycache__' -exec rm -fr {} + - -clean-test: # Remove test and coverage artifacts - rm -f .coverage - rm -f .coverage.* - -clean-venv: # Remove virtual environment - rm -rf .venv - -clean: clean-pyc clean-test clean-venv - -# Testing and Quality Checks -######################### -test: setup # Run pytest with coverage - uv run -m pytest tests --cov=$(MODULE_NAME) --cov-report=term-missing - -ty: setup # Run type checking - uv run ty check $(MODULE_NAME) - -lint: setup # Run ruff linter with auto-fix - uv run -m ruff check --fix $(MODULE_NAME) - -format: setup # Run ruff formatter - uv run -m ruff format $(MODULE_NAME) - -check: setup lint format test ty # Run all quality checks - -# Local CI Testing with act -########################### - -# Detect docker socket (Colima, Docker, or Podman) -define docker_socket -$(shell \ - if [ -S $$HOME/.colima/default/docker.sock ]; then \ - echo "$$HOME/.colima/default/docker.sock"; \ - elif [ -S /var/run/docker.sock ]; then \ - echo "/var/run/docker.sock"; \ - elif [ -S $$HOME/.docker/run/docker.sock ]; then \ - echo "$$HOME/.docker/run/docker.sock"; \ - elif [ -S /run/user/$$(id -u)/podman/podman.sock ]; then \ - echo "/run/user/$$(id -u)/podman/podman.sock"; \ - else \ - echo "/var/run/docker.sock"; \ - fi \ -) -endef - -DOCKER_SOCKET := $(docker_socket) -ACT_IMAGE := catthehacker/ubuntu:act-22.04 -ACT_ARCH := linux/amd64 - -act-check: # Check if act is installed, install if missing - @if ! which act > /dev/null 2>&1; then \ - echo "⚠️ act is not installed. Installing automatically..."; \ - $(MAKE) act-install; \ - fi - -docker-check: # Check if Docker/Podman/Colima is running - @if ! DOCKER_HOST="unix://$(DOCKER_SOCKET)" docker ps >/dev/null 2>&1; then \ - echo "❌ Cannot connect to Docker daemon"; \ - echo ""; \ - echo "Please start your container runtime first:"; \ - echo " • Docker Desktop: Open Docker Desktop app"; \ - echo " • Colima: run 'colima start'"; \ - echo " • Podman: run 'podman machine start'"; \ - echo " • Docker (Linux): run 'sudo systemctl start docker'"; \ - echo ""; \ - echo "Attempted socket: $(DOCKER_SOCKET)"; \ - exit 1; \ - fi - @echo "✓ Docker is running (socket: $(DOCKER_SOCKET))" - -act-install: ## Install act for local CI testing - @echo "Installing act (GitHub Actions local runner)..." - @if which act > /dev/null 2>&1; then \ - echo "✅ act is already installed: $$(which act)"; \ - act --version; \ - elif which brew > /dev/null 2>&1; then \ - echo "📦 Installing act via Homebrew..."; \ - brew install act; \ - else \ - echo "📦 Installing act via install script..."; \ - curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash; \ - fi - @echo "" - @echo "✅ act installed successfully!" - @echo "💡 Run 'make ci-list' to see available workflows" - @echo "💡 Run 'make ci-local' to run CI checks locally" - -ci-list: act-check docker-check ## List available GitHub Actions workflows and jobs - @echo "📋 Available workflows and jobs:" - @DOCKER_HOST="unix://$(DOCKER_SOCKET)" act -l - -ci-local: act-check docker-check ## Run CI checks locally (tests workflow) - @echo "🔬 Running local CI checks..." - @echo "Using Docker socket: $(DOCKER_SOCKET)" - @echo "Container architecture: $(ACT_ARCH)" - @echo "Image: $(ACT_IMAGE)" - @echo "" - @JOB=$${JOB:-checks}; \ - DOCKER_HOST="unix://$(DOCKER_SOCKET)" act pull_request \ - -W .github/workflows/tests.yml \ - -j $$JOB \ - --container-daemon-socket - \ - --container-architecture $(ACT_ARCH) \ - -P ubuntu-latest=$(ACT_IMAGE) - @echo "" - @echo "✅ Local CI checks complete!" - -ci-local-docs: act-check docker-check ## Run documentation build check locally - @echo "📚 Running local documentation build check..." - @DOCKER_HOST="unix://$(DOCKER_SOCKET)" act pull_request \ - -W .github/workflows/docs.yml \ - -j build-check \ - --container-daemon-socket - \ - --container-architecture $(ACT_ARCH) \ - -P ubuntu-latest=$(ACT_IMAGE) - @echo "" - @echo "✅ Documentation build check complete!" - -ci-debug: act-check docker-check ## Fast local test debugging (uses ci-debug.yml workflow) - @echo "🔧 Running CI debug workflow for fast iteration" - @echo "💡 Edit .github/workflows/ci-debug.yml to customize which tests run" - @echo "" - @DOCKER_HOST="unix://$(DOCKER_SOCKET)" act workflow_dispatch \ - -W .github/workflows/ci-debug.yml \ - --container-daemon-socket - \ - --container-architecture $(ACT_ARCH) \ - -P ubuntu-latest=$(ACT_IMAGE) - @echo "" - @echo "✅ Debug workflow complete!" - -ci-clean: ## Clean up act cache and containers - @echo "🧹 Cleaning up act cache and containers..." - @-docker ps -a | grep "act-" | awk '{print $$1}' | xargs docker rm -f 2>/dev/null || true - @-docker images | grep "act-" | awk '{print $$3}' | xargs docker rmi -f 2>/dev/null || true - @echo "✅ Cleanup complete!" - -# Documentation -############### -DOCS_PORT ?= 8000 - -docs-install: setup ## Install documentation dependencies - @echo "Installing documentation dependencies..." - uv sync --group dev - @echo "Documentation dependencies installed" - -docs-build: docs-install ## Build documentation site - @echo "Building documentation..." - uv run mkdocs build --strict - @echo "Documentation built successfully" - @echo "📄 Site location: site/" - @echo "🌐 Open site/index.html in your browser to view" - -docs-serve: docs-install ## Serve documentation locally with live reload - @echo "Starting documentation server with live reload..." - @echo "📍 Documentation will be available at:" - @echo " - Local: http://localhost:$(DOCS_PORT)" - @echo "🔄 Changes will auto-reload (press Ctrl+C to stop)" - @echo "" - @echo "💡 To use a different port: make docs-serve DOCS_PORT=9999" - uv run mkdocs serve --dev-addr 0.0.0.0:$(DOCS_PORT) - -docs-check: docs-build ## Check documentation build and links - @echo "Checking documentation..." - @echo "📊 Site size: $$(du -sh site/ | cut -f1)" - @echo "📄 Pages built: $$(find site/ -name "*.html" | wc -l)" - @echo "🔗 Checking for common issues..." - @if grep -r "404" site/ >/dev/null 2>&1; then \ - echo "⚠️ Found potential 404 errors"; \ - else \ - echo "✅ No obvious 404 errors found"; \ - fi - @if find site/ -name "*.html" -size 0 | grep -q .; then \ - echo "⚠️ Found empty HTML files"; \ - find site/ -name "*.html" -size 0; \ - else \ - echo "✅ No empty HTML files found"; \ - fi - @echo "Documentation check complete" - -docs-clean: ## Clean documentation build files - @echo "Cleaning documentation build files..." - rm -rf site/ - rm -rf .cache/ - @echo "Documentation cleaned" - -# Project Management -################## -clean-example: # Remove example code (use this to start your own project) - rm -rf $(MODULE_NAME)/example.py tests/test_example.py - touch $(MODULE_NAME)/__init__.py tests/__init__.py - -init: setup # Initialize a new project - uv run python scripts/init_project.py - -# Container Engine Support -######################## -# Auto-detect container engine (podman or docker) -CONTAINER_ENGINE ?= $(shell command -v podman >/dev/null 2>&1 && echo podman || echo docker) - -# Podman-specific adjustments -ifeq ($(CONTAINER_ENGINE),podman) - # Use podman-compose for compose functionality - COMPOSE_CMD = podman-compose - # Use host UID/GID for rootless containers - CONTAINER_USER_OPTS = --userns=keep-id - # Podman machine status check - PODMAN_MACHINE_RUNNING = $(shell podman machine list --format json 2>/dev/null | grep '"Running": true' >/dev/null && echo yes || echo no) -else - # Docker: use native compose - COMPOSE_CMD = $(CONTAINER_ENGINE) compose - # Docker: use current user's UID/GID to avoid permission issues - CONTAINER_USER_OPTS = --user $(shell id -u):$(shell id -g) -endif - -# Docker/Podman Images -##################### -IMAGE_NAME = container-registry.io/python-collab-template -IMAGE_TAG = latest - -dev-env: ensure-container-ready refresh-containers - @echo "Spinning up a dev environment using $(CONTAINER_ENGINE)..." - @$(COMPOSE_CMD) -f docker/docker-compose.yml down - @$(COMPOSE_CMD) -f docker/docker-compose.yml up -d dev - @$(CONTAINER_ENGINE) exec -ti composed_dev /bin/bash - -refresh-containers: ensure-container-ready - @echo "Rebuilding containers using $(CONTAINER_ENGINE)..." - @$(COMPOSE_CMD) -f docker/docker-compose.yml build - -rebuild-images: - @echo "Rebuilding images with the --no-cache flag using $(CONTAINER_ENGINE)..." - @$(COMPOSE_CMD) -f docker/docker-compose.yml build --no-cache - -build-image: - @echo Building dev image using $(CONTAINER_ENGINE) and tagging as ${IMAGE_NAME}:${IMAGE_TAG} - @$(COMPOSE_CMD) -f docker/docker-compose.yml down - @$(COMPOSE_CMD) -f docker/docker-compose.yml up -d dev - @$(CONTAINER_ENGINE) tag dev ${IMAGE_NAME}:${IMAGE_TAG} - -push-image: build-image - @echo Pushing image to container registry using $(CONTAINER_ENGINE) - @$(CONTAINER_ENGINE) push ${IMAGE_NAME}:${IMAGE_TAG} - -# Container Engine Info -###################### -ensure-container-ready: # Ensure container engine is ready -ifeq ($(CONTAINER_ENGINE),podman) - @echo "Checking Podman machine status..." - @if [ "$(PODMAN_MACHINE_RUNNING)" = "no" ]; then \ - echo "Podman machine is not running. Starting it..."; \ - podman machine start; \ - echo "Waiting for Podman machine to be ready..."; \ - sleep 3; \ - fi - @if ! command -v podman-compose >/dev/null 2>&1; then \ - echo "Error: podman-compose not found. Install with: brew install podman-compose"; \ - exit 1; \ - fi -else - @echo "Using Docker engine..." -endif - -container-info: # Display detected container engine and configuration - @echo "Container Engine: $(CONTAINER_ENGINE)" - @echo "Compose Command: $(COMPOSE_CMD)" - @echo "User Options: $(CONTAINER_USER_OPTS)" -ifeq ($(CONTAINER_ENGINE),podman) - @echo "Podman Machine Running: $(PODMAN_MACHINE_RUNNING)" - @echo "podman-compose Available: $(shell command -v podman-compose >/dev/null 2>&1 && echo yes || echo no)" -endif - @echo "" - @echo "To override, use: CONTAINER_ENGINE=podman make dev-env" diff --git a/README.md b/README.md index f8a62a7..af1bc29 100644 --- a/README.md +++ b/README.md @@ -1,198 +1,117 @@ -![Code Quality Checks](https://github.com/safurrier/python-collab-template/workflows/Code%20Quality%20Checks/badge.svg) [![codecov](https://codecov.io/gh/safurrier/python-collab-template/branch/master/graph/badge.svg)](https://codecov.io/gh/safurrier/python-collab-template) +# Claude PR BugScan -# Python Project Template +Reusable GitHub Actions workflow that runs [Claude Code Action](https://github.com/anthropics/claude-code-action) to review PR diffs using the official Codex Code Review prompt. -A modern Python project template with best practices for development and collaboration. +## What this does -## Features -- 🚀 Fast dependency management with [uv](https://github.com/astral-sh/uv) -- ✨ Code formatting with [ruff](https://github.com/astral-sh/ruff) -- 🔍 Type checking with [ty](https://astral.sh/blog/ty) -- 🧪 Testing with [pytest](https://github.com/pytest-dev/pytest) -- 🐳 Docker support for development and deployment -- 👷 CI/CD with GitHub Actions +- Automatically reviews PRs when they transition from **Draft → Ready for review** +- Re-runs on **new commits pushed** to an open PR +- Skips draft PRs entirely +- Posts review output back to the PR as comments +- Uses the [official Codex Code Review prompt](https://developers.openai.com/cookbook/examples/codex/build_code_review_with_codex_sdk/) verbatim -## Python Version -This template requires Python 3.9 or higher and defaults to Python 3.12. To use a different version: +## Repository layout -```bash -# List available Python versions -uv python list - -# Use a specific version (e.g., 3.11) -make setup PYTHON_VERSION=3.11 # or UV_PYTHON_VERSION=3.11 make setup - -# View installed Python versions -uv python list --installed ``` - -uv will automatically download and manage Python versions as needed. - -## Quickstart -```bash -# Clone this repo and change directory -git clone git@github.com:safurrier/python-collab-template.git my-project-name -cd my-project-name - -# Initialize a new project -make init - -# Follow the prompts to configure your project +.github/workflows/ + claude_pr_review.yml # Reusable workflow (workflow_call) — lives here + ai_pr_review.yml # Caller workflow for this repo (also an example) +prompts/ + codex_code_review_prompt.md # Official Codex prompt text (verbatim) ``` -This will: -- Configure project metadata (name, description, author) -- Handle example code (keep, simplify, or remove) -- Initialize a fresh git repository -- Set up development environment -- Configure pre-commit hooks (optional, enabled by default) +## How to use this in another repo -Pre-commit hooks will automatically run these checks before each commit: -- Type checking (ty) -- Linting (ruff) -- Formatting (ruff) -- Tests (pytest) +This repo acts as the **central workflow host**. Any other repo can call the reusable workflow with three lines — no workflow code to copy or maintain. -Alternatively, you can set up manually: -```bash -# Install dependencies and set up the environment -make setup - -# Run the suite of tests and checks -make check - -# Optional: Remove example code to start fresh -make clean-example -``` +### 1. Tag this repo first (one-time setup) -## Development Commands - -### Quality Checks ```bash -make check # Run all checks (test, ty, lint, format) -make test # Run tests with coverage -make ty # Run type checking -make lint # Run linter -make format # Run code formatter +git tag v1 +git push origin v1 ``` -### Local CI Testing - -Run GitHub Actions workflows locally before pushing using [act](https://github.com/nektos/act): +Bump to `v1.1`, `v2`, etc. for future breaking changes. -```bash -# Run full test suite locally (auto-installs act if needed) -make ci-local +### 2. Add a two-line caller workflow to each target repo -# List available workflows -make ci-list +Create `.github/workflows/ai_pr_review.yml` in the target repo: -# Run specific job -JOB=checks make ci-local +```yaml +name: Claude PR scan (Draft->Ready + Push) -# Run documentation build check -make ci-local-docs +on: + pull_request: + types: [ready_for_review, synchronize, reopened] -# Fast debugging (customize .github/workflows/ci-debug.yml) -make ci-debug +concurrency: + group: claude-pr-scan-${{ github.repository }}-${{ github.event.pull_request.number }} + cancel-in-progress: true -# Clean up act containers -make ci-clean +jobs: + scan: + if: ${{ github.event.pull_request.draft == false }} + uses: safurrier/python-collab-template/.github/workflows/claude_pr_review.yml@v1 + secrets: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} ``` -**Note:** The first run will automatically install `act` if it's not present. +The `uses:` line is the only thing that points back here. Everything else — prompt loading, checkout, Claude invocation — runs inside the reusable workflow in this repo. -**Benefits:** -- 5-20 second feedback vs. 2-5 minutes on GitHub -- Test before commit/push -- No GitHub Actions minutes consumed -- Debug workflows locally +### 3. Add the secret to each target repo -**Troubleshooting:** +**Settings → Secrets and variables → Actions → New repository secret** -*Linux: Docker permissions* -```bash -# Add your user to the docker group -sudo usermod -aG docker $USER +- Name: `ANTHROPIC_API_KEY` +- Value: your Anthropic API key -# Log out and back in for changes to take effect -# Or run: newgrp docker +That's it. Future updates to the prompt or workflow logic only need to be made here; target repos pick them up automatically on the next tagged release. -# Verify it works -docker ps -``` - -*macOS: Colima disk lock errors* -```bash -# If you get "disk in use" or similar errors: -colima stop -colima delete -colima start -``` +--- -*General: Stale act containers* -```bash -# Clean up old containers and images -make ci-clean -``` +## Customizing the prompt per repo -### Example Code -The repository includes a simple example showing: -- Type hints -- Dataclasses -- Unit tests -- Modern Python practices +To override the default Codex prompt for a specific repo, pass `prompt_override`: -To remove the example code and start fresh: -```bash -make clean-example +```yaml +jobs: + scan: + if: ${{ github.event.pull_request.draft == false }} + uses: safurrier/python-collab-template/.github/workflows/claude_pr_review.yml@v1 + secrets: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + with: + prompt_override: | + ``` -## Container Support (Docker/Podman) -### Development Environment +## Trigger policy -The project automatically detects and uses either Docker or Podman: +| Event | Runs? | +|---|---| +| PR opened as draft | No (skipped by `if` guard) | +| Draft PR gets new commits | No (skipped by `if` guard) | +| PR marked Ready for review | Yes (`ready_for_review`) | +| New commits pushed to open PR | Yes (`synchronize`) | +| Closed PR reopened | Yes (`reopened`), skipped if still draft | -```bash -make dev-env # Uses podman if available, otherwise docker +Concurrency is keyed on `github.repository + PR number` so rapid pushes cancel the in-progress scan and start fresh. -# Or explicitly choose: -CONTAINER_ENGINE=docker make dev-env -CONTAINER_ENGINE=podman make dev-env +## Versioning -# Check which engine will be used: -make container-info -``` +| Tag | Notes | +|---|---| +| `v1` | Initial release | -This creates a container with: -- All dependencies installed -- Source code mounted (changes reflect immediately) -- Development tools ready to use -- Automatic UID/GID mapping for file permissions +Breaking changes → new major tag (`v2`). Non-breaking improvements → minor tag (`v1.1`). -### Production Image -```bash -make build-image # Build production image -make push-image # Push to container registry -``` +## Permissions -## Project Structure -``` -. -├── src/ # Source code -├── tests/ # Test files -├── docker/ # Container configuration (Docker/Podman) -├── .github/ # GitHub Actions workflows -├── pyproject.toml # Project configuration -└── Makefile # Development commands -``` - -## Contributing -1. Fork the repository -2. Create a feature branch -3. Make your changes -4. Run `make check` to ensure all tests pass -5. Submit a pull request +The reusable workflow requests only: +- `contents: read` +- `pull-requests: write` +- `issues: write` ## License -This project is licensed under the MIT License - see the LICENSE file for details. + +MIT diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index 0cdf627..0000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,28 +0,0 @@ -FROM python:3.12-slim - -# Install Auxiliary Software -RUN apt-get update && apt-get install -y \ - make \ - apt-utils \ - apt-transport-https \ - curl \ - gcc \ - gnupg \ - gnupg-agent \ - graphviz \ - software-properties-common \ - vim \ - && rm -rf /var/lib/apt/lists/* - -WORKDIR /workspace - -# Copy essential files for dependency resolution to temp location -COPY pyproject.toml uv.lock /tmp/ -COPY README.md /tmp/ - -RUN pip install -U pip uv \ - && cd /tmp \ - && uv sync --system --all-extras \ - && rm -rf /tmp/pyproject.toml /tmp/uv.lock /tmp/README.md - -CMD ["/bin/bash"] diff --git a/docker/Makefile b/docker/Makefile deleted file mode 100644 index 190d502..0000000 --- a/docker/Makefile +++ /dev/null @@ -1,61 +0,0 @@ -setup: - pip install --upgrade pip - pip install -r requirements.dev - pip install -r requirements.prod - -clean-pyc: - find . -name '*.pyc' -exec rm -f {} + - find . -name '*.pyo' -exec rm -f {} + - find . -name '*~' -exec rm -f {} + - find . -name '__pycache__' -exec rm -fr {} + - -clean-test: - rm -f .coverage - rm -f .coverage.* - -clean: clean-pyc clean-test - -test: clean - py.test tests --cov=src --cov-report=term-missing --cov-fail-under 95 - -ty: - ty check src - -lint: - pylint src -j 4 --reports=y - -docs: FORCE - cd docs; sphinx-apidoc -o ./source ./src - cd docs; sphinx-build -b html ./source ./build -FORCE: - -check: test lint ty - -# Docker -######## -IMAGE_NAME = container-registry.io/python-collab-template -IMAGE_TAG = latest - -dev-env: refresh-containers - @echo "Spinning up a dev environment ." - @docker compose -f docker/docker-compose.yml down - @docker compose -f docker/docker-compose.yml up -d dev - @docker exec -ti composed_dev /bin/bash - -refresh-containers: - @echo "Rebuilding containers..." - @docker compose -f docker/docker-compose.yml build - -rebuild-images: - @echo "Rebuilding images with the --no-cache flag..." - @docker compose -f docker/docker-compose.yml build --no-cache - -build-image: - @echo Building dev image and tagging as ${IMAGE_NAME}:${IMAGE_TAG} - @docker compose -f docker/docker-compose.yml down - @docker compose -f docker/docker-compose.yml up -d dev - @docker tag dev ${IMAGE_NAME}:${IMAGE_TAG} - -push-image: build-image - @echo Pushing image to container registry - @docker push ${IMAGE_NAME}:${IMAGE_TAG} \ No newline at end of file diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml deleted file mode 100644 index eed0854..0000000 --- a/docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -version: "3.7" -services: - dev: - tty: true # Keep Alive - image: dev - container_name: composed_dev - build: - context: .. - dockerfile: docker/Dockerfile - environment: - EXAMPLE: ${EXAMPLE} - EXAMPLE_SECRET: ${EXAMPLE_SECRET} - command: /bin/bash - # Note: user mapping handled by Podman rootless automatically - # Bind whole project directory for simplicity - volumes: - - ../:/workspace:Z diff --git a/docker/template.env b/docker/template.env deleted file mode 100644 index 1b7ed75..0000000 --- a/docker/template.env +++ /dev/null @@ -1,15 +0,0 @@ -# Development environment variables -PYTHON_ENV=development - -# Example variables (replace with your own) -APP_NAME=python-collab-template -DEBUG=true - -# Secrets (do not commit actual values) -API_KEY= -DATABASE_URL= - -# Container runtime settings (optional) -# UID=${UID} # Uncomment to use your system UID -# GID=${GID} # Uncomment to use your system GID -# DOCKER_SOCK=/var/run/docker.sock # Override socket path for Podman diff --git a/docs/agents/context-files.md b/docs/agents/context-files.md new file mode 100644 index 0000000..4e45476 --- /dev/null +++ b/docs/agents/context-files.md @@ -0,0 +1,127 @@ +# Context Files Agent + +Keeps `AGENTS.md` and `CLAUDE.md` up to date in a repository. When these files are missing, Claude generates them from scratch. When they exist, Claude audits and updates them to reflect the current codebase. + +## Files + +| File | Path | +|---|---| +| Skill | `skills/context-files/SKILL.md` | +| Workflow (shared) | `.github/workflows/claude_pr_agent.yml` | + +## Skill frontmatter + +```yaml +--- +name: context-files +description: Create, update, or evaluate AGENTS.md and CLAUDE.md files for a repository. Follows WHY/WHAT/HOW structure with validated commands and progressive disclosure. +argument-hint: "[mode] [scope]" +allowed-tools: Read, Write, Edit, Bash, Grep, Glob +--- +``` + +The `argument-hint` documents what the `args:` workflow input expects. The frontmatter is stripped at load time — Claude only sees the body. + +## What it does + +Claude reads the repo structure, config files, CI workflows, and any existing context docs, then: + +1. **If no context files exist** (`auto` → `quick-start` mode): generates `AGENTS.md` with a WHY/WHAT/HOW structure and creates `CLAUDE.md` as a symlink +2. **If files already exist** (`auto` → `update + evaluate` mode): validates commands, removes stale content, improves conciseness, and creates nested `AGENTS.md` files for subdirectories with distinct workflows +3. **Commits the result** directly to the PR branch so the updated context files are part of the PR + +Every command in the generated docs is validated against Makefiles, CI configs, or package manifests before being included. + +## Output files + +| File | Role | +|---|---| +| `AGENTS.md` | Source of truth — WHY/WHAT/HOW, validated commands, progressive disclosure pointers | +| `CLAUDE.md` | Symlink to `AGENTS.md` (Claude Code auto-discovers `CLAUDE.md` files) | +| `ai_agent_docs/*.md` | Cross-cutting topic docs created when content warrants it | +| `/AGENTS.md` | Nested docs for subdirectories with distinct tooling | + +## Triggers + +| Event | Runs? | +|---|---| +| PR first opened (non-draft) | Yes | +| PR marked Ready for review | Yes | +| New commits pushed to open PR | No | +| Draft PR | No | + +## Minimal caller workflow + +```yaml +name: Context files agent + +on: + pull_request: + types: [opened, ready_for_review] + +concurrency: + group: context-files-${{ github.repository }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + context-files: + if: ${{ github.event.pull_request.draft == false }} + uses: safurrier/python-collab-template/.github/workflows/claude_pr_agent.yml@v1 + with: + skill: "context-files" + args: "auto ." + secrets: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} +``` + +## Customizing scope or mode + +Pass mode and scope via the `args:` input (substituted for `$ARGUMENTS` in the skill): + +```yaml +with: + skill: "context-files" + args: "auto src/" # scope to src/ only + # args: "quick-start ." # always generate fresh, never update + # args: "evaluate ." # audit and report in chat without writing +``` + +Modes: + +| Mode | Behavior | +|---|---| +| `auto` (default) | Generate if missing; update + evaluate if present. No confirmation prompts. | +| `quick-start` | Generate from scratch. Asks before overwriting existing files. | +| `update` | Update existing files only. | +| `evaluate` | Audit and propose improvements without modifying files. | + +## Use a local skill from the target repo + +Target repos can override the central skill entirely with their own `SKILL.md`: + +```yaml +with: + skill: ".claude/skills/context-files" # loads from target repo + args: "auto ." +``` + +Or use a raw prompt for a simpler custom format: + +```yaml +with: + prompt: | + Look at this repo and create AGENTS.md with: + 1. A one-sentence description of what the repo does + 2. How to run tests + 3. How to run the linter + Keep it under 30 lines. +``` + +## Permissions + +```yaml +permissions: + contents: write # required: commits AGENTS.md / CLAUDE.md to the branch + pull-requests: write + issues: write +``` diff --git a/docs/agents/index.md b/docs/agents/index.md new file mode 100644 index 0000000..8295d5d --- /dev/null +++ b/docs/agents/index.md @@ -0,0 +1,42 @@ +# Agents + +Each agent is a **skill** — a markdown file with YAML frontmatter — run by one generic reusable workflow. + +``` +skills/ + / + SKILL.md ← frontmatter (metadata) + body (prompt sent to Claude) + +.github/workflows/ + claude_pr_agent.yml ← single generic workflow_call for all skills +``` + +Target repos reference `claude_pr_agent.yml` and pass a `skill:` name. The workflow checks out this central repo, strips the frontmatter from the skill file, substitutes any `$ARGUMENTS`, and runs `anthropics/claude-code-action`. + +--- + +## Available skills + +| Skill | Directory | What it does | +|---|---|---| +| [PR Code Review](pr-review.md) | `skills/codex-code-review/` | Reviews diff; posts findings and correctness verdict | +| [Context Files](context-files.md) | `skills/context-files/` | Creates or updates `AGENTS.md` / `CLAUDE.md` | + +--- + +## Shared design principles + +**Every skill:** + +- Has YAML frontmatter (stripped before Claude sees it) with `name`, `description`, `argument-hint`, `allowed-tools` +- Uses `$ARGUMENTS` for runtime substitution when parameterizable +- Is independent — skills don't depend on each other + +**The generic workflow (`claude_pr_agent.yml`):** + +- Accepts a `skill:` name (central repo) or a target-repo-relative path (local skill) +- Accepts `args:` for `$ARGUMENTS` substitution +- Accepts `prompt:` as a raw prompt bypass (no skill file needed) +- Always has `contents: write` so any skill can commit files if needed + +**Adding a new agent** = adding a `skills//SKILL.md` file + a doc page. No new workflow needed. See [Contributing](../contributing.md). diff --git a/docs/agents/pr-review.md b/docs/agents/pr-review.md new file mode 100644 index 0000000..b674bfe --- /dev/null +++ b/docs/agents/pr-review.md @@ -0,0 +1,132 @@ +# PR Code Review Agent + +Reviews PR diffs using the official [Codex Code Review prompt](https://developers.openai.com/cookbook/examples/codex/build_code_review_with_codex_sdk/) and posts findings directly to the PR. + +## Files + +| File | Path | +|---|---| +| Skill | `skills/codex-code-review/SKILL.md` | +| Workflow (shared) | `.github/workflows/claude_pr_agent.yml` | + +## Skill frontmatter + +```yaml +--- +name: codex-code-review +description: Reviews PR diffs using the official Codex Code Review prompt. Posts actionable findings by category and a correctness verdict with confidence score. +argument-hint: "" +allowed-tools: Read, Grep, Glob, Bash +--- +``` + +The body after the frontmatter is the verbatim Codex prompt text. The frontmatter is stripped at load time — Claude only sees the body. + +## What it does + +Claude reads the PR diff and flags issues in these categories: + +- **Correctness** — logic errors, wrong assumptions, broken edge cases +- **Performance** — algorithmic problems, unnecessary work +- **Security** — OWASP top-10 class issues, injection risks, credential exposure +- **Maintainability** — API misuse, dead code, structural problems +- **Developer experience** — confusing interfaces, missing context + +It produces an overall verdict — `patch is correct` or `patch is incorrect` — with a confidence score between 0 and 1. + +## Triggers + +| Event | Runs? | +|---|---| +| Draft PR opened or pushed | No | +| PR marked Ready for review | Yes | +| New commits pushed to open PR | Yes | +| PR reopened (non-draft) | Yes | + +## Minimal caller workflow + +```yaml +name: Claude PR scan + +on: + pull_request: + types: [ready_for_review, synchronize, reopened] + +concurrency: + group: claude-pr-scan-${{ github.repository }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + review: + if: ${{ github.event.pull_request.draft == false }} + uses: safurrier/python-collab-template/.github/workflows/claude_pr_agent.yml@v1 + with: + skill: "codex-code-review" + secrets: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} +``` + +## Customization options + +### Use a local skill from the target repo + +Target repos can ship their own `SKILL.md` and pass its path instead of a central skill name: + +```yaml +with: + skill: ".claude/skills/security-review" # loads .claude/skills/security-review/SKILL.md +``` + +This follows the standard Claude Code skill directory convention — the `SKILL.md` file is in a named subdirectory. + +### Use a raw prompt (no skill file) + +For a one-off instruction without creating a skill file: + +```yaml +with: + prompt: | + Review this diff for REST API convention violations: + - snake_case paths only + - responses must have a top-level "data" key + Flag each violation with file and line. Give a pass/fail verdict. +``` + +### Run multiple skills sequentially + +Use `needs:` to chain skills across jobs. Each job calls the same generic workflow with a different skill: + +```yaml +jobs: + review: + if: ${{ github.event.pull_request.draft == false }} + uses: safurrier/python-collab-template/.github/workflows/claude_pr_agent.yml@v1 + with: + skill: "codex-code-review" + secrets: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + + conventions: + needs: review + if: ${{ github.event.pull_request.draft == false }} + uses: safurrier/python-collab-template/.github/workflows/claude_pr_agent.yml@v1 + with: + skill: ".claude/skills/team-conventions" + secrets: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} +``` + +## Permissions + +```yaml +permissions: + contents: write + pull-requests: write + issues: write +``` + +`contents: write` is present on the shared workflow for compatibility with write-enabled skills. The review skill itself doesn't write files. + +## Skill body (verbatim) + +> You are acting as a reviewer for a proposed code change made by another engineer. Focus on issues that impact correctness, performance, security, maintainability, or developer experience. Flag only actionable issues introduced by the pull request. When you flag an issue, provide a short, direct explanation and cite the affected file and line range. Prioritize severe issues and avoid nit-level comments unless they block understanding of the diff. After listing findings, produce an overall correctness verdict ("patch is correct" or "patch is incorrect") with a concise justification and a confidence score between 0 and 1. Ensure that file citations and line numbers are exactly correct using the tools available; if they are incorrect your comments will be rejected. diff --git a/docs/container-setup.md b/docs/container-setup.md deleted file mode 100644 index 58d4f72..0000000 --- a/docs/container-setup.md +++ /dev/null @@ -1,138 +0,0 @@ -# Container Setup Guide - -This project supports both Docker and Podman for containerized development. - -## Quick Start - -The Makefile automatically detects your container engine: - -```bash -# Automatic detection (prefers Podman if available) -make dev-env - -# Explicit engine selection -CONTAINER_ENGINE=docker make dev-env -CONTAINER_ENGINE=podman make dev-env -``` - -## Podman vs Docker - -### Key Differences - -| Feature | Docker | Podman | -|---------|--------|--------| -| Root privileges | Runs as root by default | Rootless by default | -| Daemon | Requires dockerd daemon | Daemonless | -| Security | Good with proper setup | Better default security | -| Compose support | Native | Via podman-compose | - -### When to Use Which - -**Use Docker when:** -- It's your team's standard -- You need Docker Desktop features -- You're using Docker-specific tooling - -**Use Podman when:** -- Security is a top priority -- You can't/don't want to run a daemon -- You're in a restricted environment - -## Troubleshooting - -### Permission Issues - -If you encounter permission issues with mounted volumes: - -1. **For Podman**: Should work automatically with rootless mode -2. **For Docker**: Set your UID/GID in `docker/.env`: - ```bash - echo "UID=$(id -u)" >> docker/.env - echo "GID=$(id -g)" >> docker/.env - ``` - -### Socket Issues - -If Podman can't find the Docker socket: - -```bash -# Set the socket path in your .env -echo "DOCKER_SOCK=${XDG_RUNTIME_DIR}/podman/podman.sock" >> docker/.env -``` - -### Compose Command Not Found - -For Podman, you need to install podman-compose: - -```bash -# macOS -brew install podman-compose - -# Linux -pip install podman-compose -``` - -### Podman Machine Not Running (macOS/Windows) - -Podman needs a Linux VM to run containers. The Makefile will automatically start it, but you can also manage it manually: - -```bash -# Initialize a new machine -podman machine init - -# Start the machine -podman machine start - -# Check machine status -podman machine list - -# Stop the machine -podman machine stop -``` - -### Auto-Setup with Make - -The project's Makefile handles most Podman setup automatically: - -- Checks if Podman machine is running -- Starts it if needed -- Verifies podman-compose is installed -- Uses appropriate socket paths - -Just run `make container-info` to see the current status. - -## Compatibility with Project Initialization - -The container setup is designed to work both before and after running `make init`: - -### Before `make init` -- Source code is in `src/` directory -- Container mounts entire project as `/workspace` -- All development tools work normally - -### After `make init` -- Source code moves to your project module directory (e.g., `my_project/`) -- Container setup continues to work unchanged -- Volume mounts and dependencies remain intact - -The `make init` command: -1. Renames `src/` to your project name -2. Updates import statements in tests -3. Modifies `pyproject.toml` and `Makefile` - -**The Docker/Podman setup survives this transformation** because: -- The Dockerfile doesn't hardcode directory names -- Dependencies are installed from temporary copied files -- The entire project is mounted, regardless of internal structure - -This means you can: -```bash -# Set up development environment -make dev-env - -# Initialize your project later -make init - -# Continue using the same development environment -make dev-env # Still works! -``` \ No newline at end of file diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 0000000..73ec268 --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,133 @@ +# Contributing a New Agent + +Adding an agent takes **two files and a tag bump** — a skill file and a doc page. No new workflow needed. + +## The pattern + +``` +skills/ + / + SKILL.md ← frontmatter + prompt body + +docs/agents/ + .md ← doc page +``` + +The generic `claude_pr_agent.yml` workflow already handles loading, frontmatter stripping, `$ARGUMENTS` substitution, and running Claude. You only need to write the skill content. + +--- + +## Step 1 — Write the skill + +Create `skills//SKILL.md`: + +```markdown +--- +name: your-skill-name +description: One sentence describing what this skill does and when to use it. +argument-hint: "" +allowed-tools: Read, Grep, Glob, Bash +--- + +Your prompt body here. This is what Claude receives — the frontmatter above +is stripped before the body is sent. + +Write the prompt as you'd write a Claude system prompt: +- Be specific about the output format (what Claude should post, commit, or return) +- Describe the scope (diff only? full repo? specific files?) +- If parameterizable, use $ARGUMENTS and document it in argument-hint +- Keep it focused — one skill, one job +``` + +**Frontmatter fields:** + +| Field | Required | Notes | +|---|---|---| +| `name` | Recommended | Machine name, matches directory name | +| `description` | Recommended | One line; shown in docs and used by Claude Code for auto-invocation | +| `argument-hint` | If using `$ARGUMENTS` | Documents what the `args:` input expects, e.g. `"[mode] [scope]"` | +| `allowed-tools` | Optional | Documents which Claude tools the skill uses | + +The frontmatter is **metadata for the framework** — it's stripped ("yanked") before Claude sees the content. Only the markdown body goes to Claude. + +**If the skill uses `$ARGUMENTS`:** the workflow substitutes the `args:` input at runtime via `sed`. Default `args` is empty string if not provided. + +**Permissions:** `claude_pr_agent.yml` always requests `contents: write`. No changes needed for skills that only read; skills that commit files just work. + +--- + +## Step 2 — Write the doc page + +Create `docs/agents/.md`. Include: + +- What it does (1–2 sentences) +- Files table (skill path + shared workflow) +- Skill frontmatter block (for reference) +- Trigger event table (what events make sense for this skill) +- Minimal caller workflow (copy-paste ready) +- Customization options (`args:`, `skill:` path override, `prompt:` bypass) +- Permissions note + +Then add it to the nav in `mkdocs.yml`: + +```yaml +nav: + - Agents: + - Overview: agents/index.md + - PR Code Review: agents/pr-review.md + - Context Files: agents/context-files.md + - Your Skill: agents/your-skill-name.md # add this +``` + +And add a row to the table in `docs/agents/index.md`. + +--- + +## Step 3 — Tag a new release + +```bash +git add skills// docs/agents/.md docs/agents/index.md mkdocs.yml +git commit -m "feat: add skill" +git tag v1.1 # bump to next semver +git push origin main --tags +``` + +Target repos update their `uses: ...@v1` → `@v1.1` to pick up the new skill. + +--- + +## Minimal caller workflow for a new skill + +```yaml +name: Your skill name + +on: + pull_request: + types: [ready_for_review, synchronize, reopened] + +concurrency: + group: your-skill-${{ github.repository }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + your-skill: + if: ${{ github.event.pull_request.draft == false }} + uses: safurrier/python-collab-template/.github/workflows/claude_pr_agent.yml@v1 + with: + skill: "your-skill-name" + # args: "some args" # if skill uses $ARGUMENTS + secrets: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} +``` + +--- + +## Checklist + +- [ ] `skills//SKILL.md` — frontmatter + prompt body +- [ ] `name` field matches directory name +- [ ] `argument-hint` set if skill uses `$ARGUMENTS` +- [ ] `docs/agents/.md` — doc page with trigger table and caller snippet +- [ ] Row added to `docs/agents/index.md` +- [ ] Entry added to `mkdocs.yml` nav +- [ ] New semver tag pushed diff --git a/docs/getting-started.md b/docs/getting-started.md deleted file mode 100644 index 6ea4e4a..0000000 --- a/docs/getting-started.md +++ /dev/null @@ -1,143 +0,0 @@ -# Getting Started with Python Collab Template - -This guide walks you through using this template to create a new Python project. - -## Prerequisites - -- Python 3.9 or higher -- Git -- GitHub account (for template usage) - -## Creating a Project from This Template - -### Step 1: Create Repository from Template - -1. Go to this template repository on GitHub -2. Click **"Use this template"** button -3. Choose **"Create a new repository"** -4. Fill in your repository details: - - Repository name (e.g., `my-awesome-project`) - - Description - - Public/Private visibility - -### Step 2: Clone and Initialize - -1. Clone your new repository: - ```bash - git clone https://github.com/your-username/your-project-name.git - cd your-project-name - ``` - -2. Initialize your project: - ```bash - make init - ``` - -3. Follow the interactive prompts: - - **Project name**: Enter your project name (e.g., "My Awesome Project") - - **Description**: Brief description of your project - - **Author info**: Your name and email (auto-detected from git config) - - **Example code**: Choose how to handle example code: - - Keep (useful for reference) - - Minimal (basic working example) - - Remove (clean slate) - - **Documentation**: Set up MkDocs documentation (default: yes) - - **Pre-commit hooks**: Enable quality checks on commit (default: yes) - -### Step 3: Verify Setup - -After initialization, verify everything works: - -```bash -# Run all quality checks -make check - -# If you enabled documentation -make docs-serve -``` - -## Project Structure After Initialization - -Your initialized project will have: - -``` -your-project-name/ -├── your_project_name/ # Main package (renamed from src/) -├── tests/ # Test files -├── docs/ # Documentation (if enabled) -├── .github/workflows/ # CI/CD workflows -├── pyproject.toml # Project configuration -├── Makefile # Development commands -└── README.md # Project documentation -``` - -## Development Workflow - -### Daily Development - -1. Make your changes to the code -2. Add or update tests -3. Run quality checks: - ```bash - make check - ``` -4. Update documentation if needed -5. Commit and push - -### Available Commands - -Your project comes with these make targets: - -- `make setup` - Set up development environment -- `make test` - Run tests with coverage -- `make lint` - Run linting with auto-fix -- `make format` - Format code -- `make ty` - Run type checking -- `make check` - Run all quality checks -- `make docs-serve` - Serve documentation locally (if enabled) -- `make docs-build` - Build documentation (if enabled) -- `make docs-check` - Validate documentation build (if enabled) - -### Documentation (If Enabled) - -If you chose to set up documentation: - -1. **Local development**: - ```bash - make docs-serve - # Visit http://localhost:8000 - ``` - -2. **Content organization**: - - `docs/index.md` - Project homepage - - `docs/getting-started.md` - User guide - - `docs/reference/api.md` - Auto-generated API docs - -3. **GitHub Pages setup**: - - Go to repository Settings → Pages - - Set source to "GitHub Actions" - - Documentation will auto-deploy on main branch pushes - -### CI/CD - -Your project includes GitHub Actions workflows: - -- **Quality checks** (`tests.yml`): Runs on PRs and pushes -- **Documentation** (`docs.yml`): Deploys docs to GitHub Pages (if enabled) - -## Next Steps - -1. **Update README.md** with project-specific information -2. **Add your code** in the main package directory -3. **Write tests** for your functionality -4. **Update documentation** to describe your project -5. **Set up GitHub Pages** (if documentation enabled) -6. **Configure repository settings** (branch protection, etc.) - -## Tips - -- The template includes example code you can reference or remove -- All configuration follows modern Python best practices -- The documentation system auto-generates API docs from docstrings -- Pre-commit hooks ensure code quality before commits -- Use `make` commands for consistent development workflow \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index c9fa871..4f5d6ce 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,67 +1,40 @@ -# Python Collab Template +# Claude PR Agents -A modern, collaborative Python project template with comprehensive tooling and best practices built-in. +A collection of reusable GitHub Actions workflows that attach Claude-powered automation to pull requests. Pick an agent, drop a 15-line caller workflow into your repo, add an API key, and every PR gets automated review, context file generation, or any other prompt-driven task. -## 🎯 Template Features +## Available agents -This template provides everything you need for a professional Python project: +| Agent | Trigger | What it does | +|---|---|---| +| [PR Code Review](agents/pr-review.md) | Draft→Ready, new commits pushed | Reviews the diff; posts findings and a correctness verdict | +| [Context Files](agents/context-files.md) | PR opened, Draft→Ready | Creates or updates `AGENTS.md` / `CLAUDE.md` so future agents are oriented | -- 🔧 **Modern Tooling**: UV package manager, Ruff formatting/linting, MyPy type checking -- 🧪 **Testing**: pytest with coverage reporting and CI integration -- 📚 **Documentation**: Optional MkDocs + Material theme with auto-generation -- 🚀 **CI/CD**: GitHub Actions with quality checks and automated deployment -- 🐳 **Development**: Docker support and pre-commit hooks -- 📦 **Packaging**: Modern pyproject.toml configuration with hatchling +## How it works -## 🚀 Quick Start +This repo is the **central workflow host**. Each agent lives in two files: -### Using This Template +- A **prompt** (`prompts/.md`) — the instruction text sent to Claude +- A **reusable workflow** (`.github/workflows/.yml`) — the Actions plumbing -1. **Create a new repository** from this template on GitHub -2. **Clone your new repository**: - ```bash - git clone https://github.com/your-username/your-project-name.git - cd your-project-name - ``` -3. **Initialize your project**: - ```bash - make init - ``` -4. **Follow the prompts** to customize your project - -### What `make init` Does - -The initialization script will: -- Prompt for project name, description, and author information -- Update all configuration files with your project details -- Choose how to handle example code (keep, simplify, or remove) -- Optionally set up MkDocs documentation (default: yes) -- Rename directories and update imports -- Set up git repository and pre-commit hooks - -## 📁 Template Structure +Target repos only need a thin caller workflow: ``` -python-collab-template/ -├── src/ # Source code (renamed during init) -├── tests/ # Test files -├── scripts/ # Utility scripts (including init) -├── templates/ # Documentation templates -├── docker/ # Docker configuration -├── .github/workflows/ # CI/CD automation -├── pyproject.toml # Project configuration -├── Makefile # Development commands -└── README.md # Project documentation +Your repo This repo (central host) +───────────────────────────────────── ──────────────────────────────────────── +.github/workflows/ai_pr_review.yml → .github/workflows/claude_pr_review.yml + uses: safurrier/...@v1 ├── checks out your repo + secrets: ANTHROPIC_API_KEY ├── loads prompts/codex_code_review_prompt.md + └── runs anthropics/claude-code-action@v1 ``` -## 🛠️ Available Commands +When a PR event fires, GitHub fetches the reusable workflow from this repo, runs it in your repo's context, and Claude posts its output back to the PR thread. + +## Quick start + +→ [Using in Your Repo](using.md) — step-by-step setup guide -After initialization, your project will have these commands: +## Add a new agent -- `make setup` - Set up development environment -- `make test` - Run tests with coverage -- `make check` - Run all quality checks -- `make docs-serve` - Serve documentation locally (if enabled) -- `make docs-build` - Build documentation (if enabled) +Anyone can contribute a new agent by adding a prompt file, a reusable workflow, and a doc page. -For complete usage instructions, see the [Getting Started](getting-started.md) guide. \ No newline at end of file +→ [Contributing](contributing.md) diff --git a/docs/reference/api.md b/docs/reference/api.md deleted file mode 100644 index 08b135d..0000000 --- a/docs/reference/api.md +++ /dev/null @@ -1,9 +0,0 @@ -# API Reference - -This page contains the auto-generated API documentation for the project. - -::: src - options: - show_root_heading: true - members_order: source - show_source: false \ No newline at end of file diff --git a/docs/user-stories.md b/docs/user-stories.md new file mode 100644 index 0000000..9a95a4b --- /dev/null +++ b/docs/user-stories.md @@ -0,0 +1,60 @@ +# User Stories + +## Solo developer maintaining multiple repos + +**As a developer who maintains several personal or side-project repositories,** +I want a code review pass to run automatically whenever I mark a PR ready, +so that I catch correctness issues and security problems before merging without having to remember to trigger it manually. + +**As a solo developer working quickly,** +I want the review to re-run when I push a follow-up commit to address feedback, +so that I get a fresh verdict on the updated code without creating a new PR. + +**As a solo developer who forgets to write agent context files,** +I want a workflow that notices when `AGENTS.md` / `CLAUDE.md` are missing and generates them automatically when I open a PR, +so that future AI-assisted sessions in that repo are pre-oriented without extra effort. + +--- + +## Team lead or engineering manager + +**As a team lead responsible for code quality across multiple repos,** +I want a consistent, documented review standard applied to every PR across all our repos, +so that automated reviews use the same criteria regardless of who's reviewing. + +**As a team lead,** +I want to be able to override the review prompt per-repo when one codebase has unique concerns (e.g., a security-sensitive service or a performance-critical library), +so that the review is targeted without having to fork the workflow logic. + +**As a team lead,** +I want to pin to a specific release tag of the central workflow repo, +so that I control when my team picks up changes and can test upgrades in a staging repo first. + +--- + +## Platform / DevEx engineer + +**As a DevEx engineer building internal tooling,** +I want a single central repo of reusable workflows that any team can reference with one `uses:` line, +so that maintaining or updating the review logic is a one-place change rather than a PR to every repo. + +**As a platform engineer,** +I want to contribute a new agent (e.g., a dependency audit agent, a documentation coverage agent) by following a clear pattern, +so that onboarding new automated tasks is predictable and doesn't require understanding a complex framework. + +**As a platform engineer,** +I want each agent to be independently callable, +so that repos can mix and match agents without coupling. + +--- + +## What these stories drive + +| Story theme | Feature | +|---|---| +| Automatic, no-remember trigger | `ready_for_review` + `synchronize` events; draft guard | +| No double-run on rapid pushes | Concurrency with `cancel-in-progress: true` | +| Per-repo prompt customization | `prompt_override` input on every reusable workflow | +| Version pinning | Semver tags (`@v1`, `@v1.1`) on this repo | +| Extend without duplication | New agent = prompt file + reusable workflow + doc page | +| Context files on autopilot | `context_files_agent.yml` on `opened` + `ready_for_review` | diff --git a/docs/using.md b/docs/using.md new file mode 100644 index 0000000..994e31f --- /dev/null +++ b/docs/using.md @@ -0,0 +1,193 @@ +# Using in Your Repo + +Each skill is independent — add only the ones you want. All skills run through the same generic `claude_pr_agent.yml` workflow. + +## Prerequisites + +- An Anthropic API key ([get one here](https://console.anthropic.com/)) +- Write access to your target repo +- This central repo tagged at `v1` (one-time, see below) + +--- + +## One-time: tag this repo + +Before any target repo can call a reusable workflow, you need a stable tag to pin to. + +```bash +git tag v1 +git push origin v1 +``` + +Future releases follow the same pattern: `v1.1`, `v2`, etc. Target repos stay pinned to their chosen tag until you update the `uses:` line. + +--- + +## Add skills to a repo + +### Step 1 — Create the caller workflow + +Create `.github/workflows/claude_agents.yml` in your target repo. Each skill is a separate job pointing at the same `claude_pr_agent.yml` workflow. + +**Code review only:** + +```yaml +name: Claude PR scan + +on: + pull_request: + types: [ready_for_review, synchronize, reopened] + +concurrency: + group: claude-pr-scan-${{ github.repository }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + review: + if: ${{ github.event.pull_request.draft == false }} + uses: safurrier/python-collab-template/.github/workflows/claude_pr_agent.yml@v1 + with: + skill: "codex-code-review" + secrets: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} +``` + +**Context files only:** + +```yaml +name: Context files agent + +on: + pull_request: + types: [opened, ready_for_review] + +concurrency: + group: context-files-${{ github.repository }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + context-files: + if: ${{ github.event.pull_request.draft == false }} + uses: safurrier/python-collab-template/.github/workflows/claude_pr_agent.yml@v1 + with: + skill: "context-files" + args: "auto ." + secrets: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} +``` + +**Both skills, sequenced** — review runs first, context-files waits for it: + +```yaml +name: Claude PR agents + +on: + pull_request: + types: [opened, ready_for_review, synchronize, reopened] + +concurrency: + group: claude-agents-${{ github.repository }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + review: + if: ${{ github.event.pull_request.draft == false }} + uses: safurrier/python-collab-template/.github/workflows/claude_pr_agent.yml@v1 + with: + skill: "codex-code-review" + secrets: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + + context-files: + needs: review + if: ${{ github.event.pull_request.draft == false }} + uses: safurrier/python-collab-template/.github/workflows/claude_pr_agent.yml@v1 + with: + skill: "context-files" + args: "auto ." + secrets: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} +``` + +### Step 2 — Add the secret + +In your target repo: **Settings → Secrets and variables → Actions → New repository secret** + +| Field | Value | +|---|---| +| Name | `ANTHROPIC_API_KEY` | +| Value | Your Anthropic API key | + +### Step 3 — Open a PR and verify + +Open a draft PR, then mark it **Ready for review**. The workflow appears in the **Actions** tab and Claude posts output to the PR within a minute or two. + +--- + +## Customization options + +### Use a raw prompt (no skill file) + +For a one-off instruction without creating a skill: + +```yaml +with: + prompt: | + Review this diff for security issues only. + Flag any use of eval(), exec(), unsanitized SQL, or raw HTTP calls. + Give a pass/fail verdict. +``` + +### Use a local skill from the target repo + +Target repos can define their own `SKILL.md` files and pass the path: + +```yaml +with: + skill: ".claude/skills/team-conventions" # .claude/skills/team-conventions/SKILL.md + # OR + skill: "./skills/security-review" # skills/security-review/SKILL.md + # OR + skill: "./prompts/quick-check.md" # flat .md file, no directory +``` + +This follows the Claude Code skill convention: paths containing `/` are resolved from the target repo checkout; bare names are resolved from the central repo's `skills/` directory. + +### Mix central and local skills + +```yaml +jobs: + review: + uses: safurrier/python-collab-template/.github/workflows/claude_pr_agent.yml@v1 + with: + skill: "codex-code-review" # central repo skill + secrets: { ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} } + + conventions: + needs: review + uses: safurrier/python-collab-template/.github/workflows/claude_pr_agent.yml@v1 + with: + skill: ".claude/skills/api-conventions" # target repo local skill + secrets: { ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} } +``` + +--- + +## Trigger policy reference + +| Event | Review skill | Context files skill | +|---|---|---| +| PR opened as draft | Skipped | Skipped | +| Draft PR gets new commits | Skipped | Skipped | +| PR marked Ready for review | Runs | Runs | +| New commits pushed to open PR | Runs | Not triggered | +| Closed PR reopened (non-draft) | Runs | Not triggered | +| PR first opened (non-draft) | Not triggered | Runs | + +--- + +## Private vs public repos + +Use `pull_request` (not `pull_request_target`) — correct for repos without forks. The `ANTHROPIC_API_KEY` secret is accessible to `pull_request` workflows from branches in the same repo. + +If this central workflow repo is **private**, target repos need access: **Settings → Actions → Access → Accessible from repositories in your account**. diff --git a/mkdocs.yml b/mkdocs.yml index e5582e0..aa3d27b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,27 +1,12 @@ -site_name: Python Collab Template +site_name: Claude PR Agents +site_description: Reusable GitHub Actions workflows for AI-powered PR automation using Claude site_url: https://safurrier.github.io/python-collab-template/ -site_description: A collaborative Python project template with modern tooling -site_author: safurrier - -repo_name: safurrier/python-collab-template repo_url: https://github.com/safurrier/python-collab-template +repo_name: safurrier/python-collab-template edit_uri: edit/main/docs/ theme: name: material - features: - - navigation.tabs - - navigation.tabs.sticky - - navigation.sections - - navigation.top - - navigation.tracking - - search.suggest - - search.highlight - - search.share - - toc.follow - - content.action.edit - - content.code.copy - - content.code.annotate palette: - scheme: default primary: indigo @@ -35,46 +20,34 @@ theme: toggle: icon: material/brightness-4 name: Switch to light mode - font: - text: Roboto - code: Roboto Mono - icon: - logo: material/library + features: + - navigation.tabs + - navigation.sections + - navigation.top + - content.code.copy + - content.code.annotate + - toc.follow -plugins: - - search - - mkdocstrings: - handlers: - python: - paths: [src] - options: - show_source: true - show_root_heading: true - merge_init_into_class: true - docstring_style: google - show_signature_annotations: true - separate_signature: true +nav: + - Home: index.md + - User Stories: user-stories.md + - Using in Your Repo: using.md + - Agents: + - Overview: agents/index.md + - PR Code Review: agents/pr-review.md + - Context Files: agents/context-files.md + - Contributing: contributing.md markdown_extensions: - - admonition - - attr_list - - pymdownx.details - - pymdownx.superfences - - pymdownx.tabbed: - alternate_style: true - pymdownx.highlight: anchor_linenums: true - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.superfences + - admonition + - pymdownx.details + - attr_list + - md_in_html + - tables - toc: permalink: true - -nav: - - Home: index.md - - Getting Started: getting-started.md - - Reference: - - API Documentation: reference/api.md - -extra: - social: - - icon: fontawesome/brands/github - link: https://github.com/safurrier/python-collab-template \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 445c5a9..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,78 +0,0 @@ -[project] -name = "python-collab-template" -version = "0.1.0" -description = "Add your description here" -authors = [ - { name = "alex furrier", email = "afurrier@gmail.com" } -] -requires-python = ">= 3.9" -readme = "README.md" -dependencies = [] # No runtime dependencies needed for example - -[project.optional-dependencies] -dev = [ - "ty>=0.0.2", # Type checking - "pytest>=8.1.1", # Testing - "pytest-cov>=5.0.0", # Coverage reporting - "ruff>=0.3.0", # Linting and formatting - "pre-commit>=3.6.0", # Pre-commit hooks - "tomli>=2.0.1", # TOML reading - "tomli-w>=1.0.0", # TOML writing -] - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[dependency-groups] -dev = [ - "mkdocs-material>=9.6.14", - "mkdocstrings[python]>=0.26.1", -] - -[tool.hatch.build.targets.wheel] -packages = ["src"] - -[tool.ruff] -line-length = 88 -target-version = "py39" - -[tool.ruff.lint] -select = [ - "E", # pycodestyle errors - "W", # pycodestyle warnings - "F", # pyflakes - "I", # isort - "B", # flake8-bugbear - "C4", # flake8-comprehensions - "UP", # pyupgrade - "TID", # flake8-tidy-imports - "E402", # module-import-not-at-top-of-file - "N", # pep8-naming - "S", # flake8-bandit - "PTH", # flake8-use-pathlib - "RUF", # ruff-specific rules - "ICN", # flake8-import-conventions -] - -ignore = [ - "E501", # line too long (handled by line-length) - "TID252", # prefer absolute imports (keep relative for internal imports) - "S101", # assert used (acceptable in tests) -] - -[tool.ruff.lint.flake8-tidy-imports] -# Prevent specific problematic imports -banned-api = {} - -[tool.ruff.lint.isort] -known-first-party = ["python-collab-template"] - -[tool.ty.environment] -python-version = "3.9" - -[tool.ty.src] -include = ["src"] - -[tool.ty.terminal] -error-on-warning = true diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index 6860cba..0000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,46 +0,0 @@ -# This file was autogenerated by uv via the following command: -# uv pip compile pyproject.toml --extra dev -o requirements-dev.txt -cfgv==3.4.0 - # via pre-commit -coverage==7.9.1 - # via pytest-cov -distlib==0.3.9 - # via virtualenv -filelock==3.18.0 - # via virtualenv -identify==2.6.12 - # via pre-commit -iniconfig==2.1.0 - # via pytest -nodeenv==1.9.1 - # via pre-commit -packaging==25.0 - # via pytest -platformdirs==4.3.8 - # via virtualenv -pluggy==1.6.0 - # via - # pytest - # pytest-cov -pre-commit==4.2.0 - # via python-collab-template (pyproject.toml) -pygments==2.19.1 - # via pytest -pytest==8.4.1 - # via - # python-collab-template (pyproject.toml) - # pytest-cov -pytest-cov==6.2.1 - # via python-collab-template (pyproject.toml) -pyyaml==6.0.2 - # via pre-commit -ruff==0.12.0 - # via python-collab-template (pyproject.toml) -tomli==2.2.1 - # via python-collab-template (pyproject.toml) -tomli-w==1.2.0 - # via python-collab-template (pyproject.toml) -ty==0.0.2 - # via python-collab-template (pyproject.toml) -virtualenv==20.31.2 - # via pre-commit diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 9a56360..0000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -# This file was autogenerated by uv via the following command: -# uv pip compile pyproject.toml -o requirements.txt diff --git a/scripts/init_project.py b/scripts/init_project.py deleted file mode 100755 index cbde75d..0000000 --- a/scripts/init_project.py +++ /dev/null @@ -1,389 +0,0 @@ -import os -import subprocess -import sys -from pathlib import Path -from typing import Optional -import tomli -import tomli_w - - -def prompt_with_default(prompt: str, default: str) -> str: - """Prompt for input with a default value.""" - response = input(f"{prompt} [{default}]: ").strip() - return response if response else default - - -def get_git_config(key: str) -> Optional[str]: - """Get git config value.""" - try: - return subprocess.check_output( - ["git", "config", "user."+key], text=True - ).strip() - except subprocess.CalledProcessError: - return None - - -def update_pyproject_toml( - project_name: str, - project_description: str, - author_name: str, - author_email: str -) -> None: - """Update pyproject.toml with new project information.""" - pyproject_path = Path("pyproject.toml") - - # Read existing toml - with open(pyproject_path, "rb") as f: - config = tomli.load(f) - - # Update project information - config["project"]["name"] = project_name - config["project"]["description"] = project_description - config["project"]["authors"] = [ - {"name": author_name, "email": author_email} - ] - - # Write updated toml - with open(pyproject_path, "wb") as f: - tomli_w.dump(config, f) - - -def run_command(command: str) -> None: - """Run a shell command and exit if it fails.""" - try: - subprocess.run(command, shell=True, check=True) - except subprocess.CalledProcessError as e: - print(f"Error running command: {command}") - print(f"Error: {e}") - sys.exit(1) - - -def template_file(template_path: str, output_path: str, replacements: dict) -> None: - """Template a file by replacing placeholders with values.""" - with open(template_path, "r") as f: - content = f.read() - - for placeholder, value in replacements.items(): - content = content.replace(f"{{{placeholder}}}", value) - - # Ensure parent directory exists (only if there is a parent directory) - parent_dir = os.path.dirname(output_path) - if parent_dir: - os.makedirs(parent_dir, exist_ok=True) - - with open(output_path, "w") as f: - f.write(content) - - -def setup_documentation( - project_name: str, - project_description: str, - author_name: str, - author_email: str, - project_module_name: str, - github_username: str = "your-username" -) -> None: - """Set up documentation with MkDocs + Material theme.""" - print("📚 Setting up documentation...") - - # Define template replacements - replacements = { - "project_name": project_name, - "project_description": project_description, - "author_name": author_name, - "author_email": author_email, - "project_module_name": project_module_name, - "github_username": github_username, - } - - # Template and create documentation files - template_file("templates/mkdocs.yml.template", "mkdocs.yml", replacements) - template_file("templates/docs/index.md.template", "docs/index.md", replacements) - template_file("templates/docs/getting-started.md.template", "docs/getting-started.md", replacements) - template_file("templates/docs/reference/api.md.template", "docs/reference/api.md", replacements) - template_file("templates/.github/workflows/docs.yml.template", ".github/workflows/docs.yml", replacements) - - # Add documentation dependencies to pyproject.toml - pyproject_path = Path("pyproject.toml") - with open(pyproject_path, "rb") as f: - config = tomli.load(f) - - # Ensure dependency-groups exists - if "dependency-groups" not in config: - config["dependency-groups"] = {} - - # Add docs dependencies to dev group - if "dev" not in config["dependency-groups"]: - config["dependency-groups"]["dev"] = [] - - docs_deps = [ - "mkdocs-material>=9.6.14", - "mkdocstrings[python]>=0.26.1", - ] - - for dep in docs_deps: - if dep not in config["dependency-groups"]["dev"]: - config["dependency-groups"]["dev"].append(dep) - - with open(pyproject_path, "wb") as f: - tomli_w.dump(config, f) - - print("✅ Documentation setup complete") - print(" - MkDocs configuration created") - print(" - Documentation structure created") - print(" - GitHub Actions workflow added") - print(" - Dependencies added to pyproject.toml") - - -def main() -> None: - print("🚀 Initializing new Python project...") - - # Get project information - project_name = prompt_with_default( - "Project name", "my-python-project" - ) - project_description = prompt_with_default( - "Project description", "A Python project" - ) - author_name = prompt_with_default( - "Author name", get_git_config("name") or "Your Name" - ) - author_email = prompt_with_default( - "Author email", get_git_config("email") or "your.email@example.com" - ) - - # Update project information - print("📝 Updating project configuration...") - update_pyproject_toml( - project_name, - project_description, - author_name, - author_email - ) - - # Handle example code - code_choice = prompt_with_default( - "How would you like to handle example code?\n" - "1. Keep example code (useful for reference)\n" - "2. Create minimal placeholder test (ensures checks pass)\n" - "3. Remove all example code (clean slate)\n" - "Choose option (1/2/3)", "1" - ) - - # Create module directory with project name (replacing src) - project_module_name = project_name.replace("-", "_").lower() - - # Always update the Makefile to use the new module name - print(f"🔧 Updating Makefile to use module name: {project_module_name}") - makefile_path = Path("Makefile") - with open(makefile_path, "r") as f: - makefile_content = f.read() - - # Replace module name in Makefile - updated_makefile = makefile_content.replace("MODULE_NAME := src", f"MODULE_NAME := {project_module_name}") - - with open(makefile_path, "w") as f: - f.write(updated_makefile) - - # Always update pyproject.toml to point to the new module directory - print(f"📦 Updating pyproject.toml for module: {project_module_name}") - pyproject_path = Path("pyproject.toml") - with open(pyproject_path, "rb") as f: - config = tomli.load(f) - - # Update packages from src to new module name - if "tool" in config and "hatch" in config["tool"] and "build" in config["tool"]["hatch"] and "targets" in config["tool"]["hatch"]["build"] and "wheel" in config["tool"]["hatch"]["build"]["targets"]: - config["tool"]["hatch"]["build"]["targets"]["wheel"]["packages"] = [project_module_name] - - # Update ruff known-first-party if ruff config exists - if "tool" in config and "ruff" in config["tool"] and "lint" in config["tool"]["ruff"] and "isort" in config["tool"]["ruff"]["lint"]: - config["tool"]["ruff"]["lint"]["isort"]["known-first-party"] = [project_module_name] - - with open(pyproject_path, "wb") as f: - tomli_w.dump(config, f) - - # Create the new module directory if it doesn't exist - if not os.path.exists(project_module_name): - print(f"📁 Creating module directory: {project_module_name}") - os.mkdir(project_module_name) - # Create __init__.py - with open(f"{project_module_name}/__init__.py", "w") as f: - f.write(f'"""Main package for {project_name}."""\n') - - # Copy src content to new module directory if src exists - if os.path.exists("src") and project_module_name != "src": - print(f"📦 Copying content from src to {project_module_name}...") - for item in os.listdir("src"): - src_path = os.path.join("src", item) - dest_path = os.path.join(project_module_name, item) - - if os.path.isfile(src_path): - with open(src_path, "r") as src_file: - content = src_file.read() - with open(dest_path, "w") as dest_file: - dest_file.write(content) - - # Remove the old src directory after copying - print("🗑️ Removing old src directory...") - run_command("rm -rf src") - - if code_choice == "2": - print("📝 Creating minimal placeholder test...") - # Create minimal module - with open(f"{project_module_name}/example.py", "w") as f: - f.write("""def add(a: int, b: int) -> int: - \"\"\"Add two numbers.\"\"\" - return a + b -""") - - # Create minimal test - with open("tests/test_example.py", "w") as f: - f.write(f"""from {project_module_name}.example import add - -def test_add(): - assert add(1, 2) == 3 -""") - elif code_choice == "3": - print("🧹 Removing all example code...") - run_command("make clean-example") - # Create __init__.py in tests - with open("tests/__init__.py", "w") as f: - f.write("") - else: - print("📚 Updating example code imports for new module name...") - # Update example.py to use new module name - if os.path.exists("src/example.py"): - with open("src/example.py", "r") as f: - example_content = f.read() - # Save it to new module directory - with open(f"{project_module_name}/example.py", "w") as f: - f.write(example_content) - - # Update test imports - if os.path.exists("tests/test_example.py"): - with open("tests/test_example.py", "r") as f: - test_content = f.read() - updated_test = test_content.replace("from src.", f"from {project_module_name}.") - with open("tests/test_example.py", "w") as f: - f.write(updated_test) - - # Documentation setup - docs_choice = prompt_with_default( - "\nWould you like to set up documentation with MkDocs?\n" - "This includes:\n" - "- Material theme documentation site\n" - "- Auto-generated API documentation\n" - "- GitHub Pages deployment\n" - "- Local development server\n" - "\nSet up documentation? (Y/n)", "y" - ) - - docs_enabled = docs_choice.lower() in ('y', 'yes', '') - if docs_enabled: - # Extract GitHub username from git config or use placeholder - try: - github_url = subprocess.check_output(["git", "config", "remote.origin.url"], text=True).strip() - if "github.com" in github_url: - # Extract username from GitHub URL - if github_url.startswith("git@github.com:"): - github_username = github_url.split(":")[1].split("/")[0] - elif github_url.startswith("https://github.com/"): - github_username = github_url.split("/")[3] - else: - github_username = "your-username" - else: - github_username = "your-username" - except (subprocess.CalledProcessError, IndexError): - github_username = "your-username" - - setup_documentation( - project_name, - project_description, - author_name, - author_email, - project_module_name, - github_username - ) - else: - print("⏩ Skipping documentation setup") - - # Get current directory name and handle renaming - current_dir = os.path.basename(os.getcwd()) - if current_dir == "python-collab-template" or current_dir == "python-project-test": - parent_dir = os.path.dirname(os.getcwd()) - new_dir = os.path.join(parent_dir, project_name) - print(f"📁 Renaming project directory to {project_name}...") - if os.path.exists(new_dir): - print(f"⚠️ Directory {project_name} already exists. Keeping current directory name.") - else: - # Update source code directory references in Makefile - makefile_path = Path("Makefile") - with open(makefile_path, "r") as f: - makefile_content = f.read() - - # Replace any hardcoded references to python-collab-template in the Makefile - updated_makefile = makefile_content.replace("python-collab-template", project_name) - - with open(makefile_path, "w") as f: - f.write(updated_makefile) - - # Now rename the directory - os.chdir(parent_dir) - os.rename(current_dir, project_name) - os.chdir(project_name) - - # Install dependencies and set up environment - print("🔨 Setting up development environment...") - run_command("make setup") - - # Configure pre-commit hooks - precommit_choice = prompt_with_default( - "\nWould you like to enable pre-commit hooks?\n" - "These hooks run automatically before each commit to ensure code quality:\n" - "- Type checking (ty)\n" - "- Linting (ruff)\n" - "- Formatting (ruff)\n" - "- Tests (pytest)\n" - "\nEnable pre-commit hooks? (y/n)", "y" - ) - - # Initialize new git repository - print("🔄 Initializing git repository...") - if os.path.exists(".git"): - run_command("rm -rf .git") - run_command("git init") - - if precommit_choice.lower() in ('y', 'yes'): - print("🔧 Setting up pre-commit hooks...") - run_command("uv run pre-commit install") - else: - print("⏩ Skipping pre-commit hooks setup") - - # Initial commit without running pre-commit hooks - run_command("git add .") - run_command('git commit -m "feat: Initial project setup" --no-verify') - - print("✨ Project initialized successfully!") - next_steps = [ - "1. Update README.md with your project details", - "2. Review and update CHANGELOG.md", - f"3. Start adding your code in {project_module_name}/", - "4. Run 'make check' to verify everything works" - ] - - if docs_enabled: - next_steps.extend([ - "5. Serve documentation locally: 'make docs-serve'", - "6. Update docs content in docs/ directory", - "7. Enable GitHub Pages in repository settings for automatic deployment" - ]) - - print("\nNext steps:") - for step in next_steps: - print(step) - - print("\nHappy coding! 🎉") - - -if __name__ == "__main__": - main() diff --git a/skills/codex-code-review/SKILL.md b/skills/codex-code-review/SKILL.md new file mode 100644 index 0000000..73b1ad5 --- /dev/null +++ b/skills/codex-code-review/SKILL.md @@ -0,0 +1,8 @@ +--- +name: codex-code-review +description: Reviews PR diffs using the official Codex Code Review prompt. Posts actionable findings by category and a correctness verdict with confidence score. +argument-hint: "" +allowed-tools: Read, Grep, Glob, Bash +--- + +You are acting as a reviewer for a proposed code change made by another engineer. Focus on issues that impact correctness, performance, security, maintainability, or developer experience. Flag only actionable issues introduced by the pull request. When you flag an issue, provide a short, direct explanation and cite the affected file and line range. Prioritize severe issues and avoid nit-level comments unless they block understanding of the diff. After listing findings, produce an overall correctness verdict ("patch is correct" or "patch is incorrect") with a concise justification and a confidence score between 0 and 1. Ensure that file citations and line numbers are exactly correct using the tools available; if they are incorrect your comments will be rejected. diff --git a/skills/context-files/SKILL.md b/skills/context-files/SKILL.md new file mode 100644 index 0000000..fefca43 --- /dev/null +++ b/skills/context-files/SKILL.md @@ -0,0 +1,410 @@ +--- +name: context-files +description: Create, update, or evaluate AGENTS.md and CLAUDE.md files for a repository. Follows WHY/WHAT/HOW structure with validated commands and progressive disclosure. +argument-hint: "[mode] [scope]" +allowed-tools: Read, Write, Edit, Bash, Grep, Glob +--- + +You are operating inside a real codebase. Your job is to produce and maintain **high-signal agent onboarding memory files**: + +* `AGENTS.md` (cross-tool, open format) +* `CLAUDE.md` (Claude Code context file) + +You must follow these principles: + +* **Stateless onboarding**: assume you know nothing about this repo until you read it; the files you write should onboard future agent sessions. +* **Less is more**: keep contents concise and universally applicable. Target **<150 lines** for a root file when possible; **hard stop at ~300 lines** unless you have a specific, justified reason. +* **WHY / WHAT / HOW**: + + * WHY: what this repo is for + * WHAT: map of the repo (where things live) + * HOW: how to work here (commands + validation + workflows) +* **Progressive disclosure**: do not stuff everything into AGENTS/CLAUDE. Prefer links/pointers to authoritative docs/config/scripts inside the repo. **Actively create nested AGENTS.md** for module-specific docs (Claude Code auto-discovers these). **Actively create ai_agent_docs/** for cross-cutting concerns that span modules. Both can exist at any scope level. Lean toward creating these files rather than cramming content into the root file—they keep individual files lean and focused. Each nested file should be small and self-contained. +* **Claude is not a linter**: do not write verbose style guides. Prefer deterministic tools (formatters/linters/typecheckers) and tell how to run them. +* **Validate anything you claim**: + + * If you include a command, validate it exists (scripts/Makefile/Taskfile/CI docs/etc). + * Treat install/bootstrap commands as potentially networked and stateful; validate by confirming scripts/targets exist and prefer --version / --help over executing installs unless the user explicitly asks + * Prefer running a cheap validation (`--help`, list scripts, dry-run) and record whether you executed it. + * If you cannot execute a command in this environment, mark it as **"not executed"** and explain what prevented it. + +Security/ops guardrails: + +* Never write secrets (API keys, tokens, connection strings) into AGENTS/CLAUDE. +* Do not run destructive shell commands. +* Prefer offline, unit-level validation; avoid networked/E2E/integration/data-mutating commands unless the user explicitly asks. +* If you detect security-sensitive instructions in existing files, propose safer rewrites. + +--- + +## Inputs (from slash command arguments) + +RAW ARGUMENTS: +`$ARGUMENTS` + +### Help mode (special) + +If `$ARGUMENTS` contains `--help` anywhere (or is exactly `help`), do **not** modify the repo. + +Instead, print a compact usage guide: + +* What the command does +* Modes and what they mean +* Target flags +* How `scope` works +* 6–10 common examples + +Default assumption when not using `--help`: mode defaults to `auto`. + +### Parsing rules + + +Parse `$ARGUMENTS` by: + 1) Extract known flags anywhere: --help, --agents, --claude, --both + 2) Remove them from the token list + 3) Parse remaining tokens as: [mode] [scope...] + - scope can be one or more paths (space or comma separated) + 4) Defaults: mode=auto, scope="." + * `--both` (default) + * `--agents` (only AGENTS.md) + * `--claude` (only CLAUDE.md) + +If mode is omitted, assume `auto`. + +**Multi-scope behavior**: +- When multiple scopes provided: create/update AGENTS.md in each scope +- Root AGENTS.md: update to reference nested docs (if it exists) +- Each scope is processed independently following the same rules + +**Examples**: +``` +/agent-docs auto config/nvim config/zsh config/ai-config +/agent-docs auto config/nvim, config/zsh, config/ai-config +``` + +Mode behaviors: + +* **quick-start** + + * Create missing file(s) from scratch. + * If scope is ambiguous (monorepo, multiple apps, unclear root), ask the user with 2–3 best-guess options. + * **CLAUDE.md consolidation prompt**: If CLAUDE.md exists as a standalone file (not a symlink), ask the user if they want to consolidate it into AGENTS.md and replace CLAUDE.md with a symlink. +* **update** + + * If file(s) exist, update them to reflect current reality. + * Validate that commands/paths referenced are still valid; fix or propose changes when stale. + * **CLAUDE.md consolidation prompt**: If CLAUDE.md exists as a standalone file (not a symlink), ask the user if they want to consolidate it into AGENTS.md and replace CLAUDE.md with a symlink. +* **evaluate** + + * Do everything in update mode, plus: + * Evaluate file(s) against the principles above; propose improvements (and apply safe improvements when appropriate). + * **CLAUDE.md consolidation prompt**: If CLAUDE.md exists as a standalone file (not a symlink), ask the user if they want to consolidate it into AGENTS.md and replace CLAUDE.md with a symlink. +* **auto** + + * If neither AGENTS.md nor CLAUDE.md exists in the chosen scope: behave like quick-start. + * Otherwise: behave like update + evaluate. + * **Do not ask the user for permission/confirmation.** Make best-judgment changes and then report exactly what changed and why. + * **CLAUDE.md consolidation (auto only)**: If CLAUDE.md exists as a standalone file (not a symlink to AGENTS.md), automatically consolidate its content into AGENTS.md and replace CLAUDE.md with a symlink to AGENTS.md. Report what content was merged. + * **Nested docs creation (auto mode default behavior)**: + - **Always** scan for directories that have their own workflows, commands, or tools — create nested `AGENTS.md` + `CLAUDE.md` symlink in each + - **Always** identify cross-cutting topics (architecture, conventions, testing, deployment) — create `ai_agent_docs/` files for them + - When multiple scopes provided: create nested `AGENTS.md` in each scope + - Reference all nested docs in the root AGENTS.md + - Prefer more smaller files over fewer bloated ones; each file stays lean and focused + +--- + +## Execution plan (follow this sequence) + +### 1) Determine root and scope precisely + +* If this is a git repo, find the git root (preferred anchor). If not, treat the provided scope directory as the anchor. +* Interpret `scope` as the **target sub-area** when provided (e.g., `services/payments`, `packages/foo`, `src/moduleX`). +* Detect monorepo signals (workspaces, multiple services/apps, many package manifests). +* Decide whether the user likely intends: + + * root-only onboarding docs, or + * root + subproject-specific docs (nested instructions) + +**Scope selection rules:** + +* If the user provided a `scope` path, anchor your work there. + + * Prefer writing onboarding docs at that scope's project root (e.g., the nearest directory containing a package/build manifest, or the nearest meaningful boundary like `services//`, `packages//`). +* quick-start/update/evaluate: if multiple reasonable anchors exist for the given scope, ask the user to pick (2–3 best-guess options). +* auto: pick the best default without asking: + + 1. If `scope` is provided: write docs for that scope anchor. + 2. Else write a root file at the git root. + 3. Proactively create nested AGENTS.md files for directories that have their own workflows, commands, or configuration. Also create `ai_agent_docs/` for cross-cutting topics. Prefer more smaller files over fewer bloated ones. + +**Optional inference when `scope` is omitted (best effort, read-only):** + +* If available, use cheap git signals to infer the most relevant sub-area: + + * `git status --porcelain` / `git diff --name-only` to see currently-changed files. + * `git log -n 20 --name-only` to see recently-touched paths (if fast). + * If one directory dominates the touched paths, treat that as an implied scope anchor. +* If signals are absent/ambiguous, fall back to git root. + +When you infer scope this way, state explicitly what signal you used and what you inferred. + +**Scope guardrail:** + +* **Respect explicit scope**: When a user provides a specific path (e.g., "create AGENTS.md for discord_api/"), only create docs within that scope. Do not create docs in parent directories, sibling directories, or the repo root unless the user asks. + +**Context management principles** (use judgment, not hard limits): + +* Every file should earn its existence — if it would be near-empty or just repeat a parent, fold it in +* Deeper nesting should mean more specific content, not structural boilerplate +* `ai_agent_docs/` files should cover topics that genuinely span multiple modules; don't create them as just another dumping ground + +### 2) Discover authoritative context (minimal, targeted reading) + +Prioritize: + +* README / docs that describe purpose and setup +* build/test/lint/typecheck configs and scripts: + + * package manifests (package.json, pyproject.toml, Cargo.toml, go.mod, pom.xml, build.gradle, etc.) + * Makefile/Taskfile/justfile + * CI workflows (.github/workflows/* or equivalents) +* repository layout (top-level directories, key packages/apps) + +Be systematic: + +* Use Glob/Grep to locate: + + * "how to run", "development", "testing", "lint", "format", "typecheck", "build", "ci" + * references to dev servers, environment setup, local DBs, etc. + +### 3) Derive a minimal set of "common commands" + +Goal: include only commands that are broadly useful and stable. + +Typical buckets (only include what exists): + +* install / bootstrap +* dev server / local run +* test +* lint / format +* typecheck +* build +* (optional) e2e / integration tests +* (optional) "single test" patterns / targeting + +Validation requirements: + +* For each command you plan to list: + + * confirm it exists (e.g., package.json scripts, Makefile target, CI step) + * if feasible, run a **non-destructive validation** (`--help`, list scripts/targets, or dry-run) + * record status as one of: + + * ✅ executed successfully + * ⚠️ executed but failed (include reason + fix suggestion) + * ⏸️ not executed (include why and how to run) + +**Additional rule for TEST commands (required when feasible):** + +* If you list a test command, you must attempt a **minimal, safe, fast test execution** to prove the harness works. +* Constraints (hard): + + * Avoid anything likely to be **long-running**, **networked**, **E2E**, **integration**, **UI**, or **data-mutating**. + * Do NOT run targets/scripts containing (case-insensitive) keywords like: + + * `e2e`, `integration`, `playwright`, `cypress`, `selenium`, `puppeteer`, `browser` + * `load`, `stress`, `perf`, `benchmark` + * `docker`, `compose`, `k8s`, `helm`, `terraform` + * `migrate`, `seed`, `reset`, `drop`, `provision` +* Selection strategy (in order): + + 1. Prefer an explicitly "fast/unit/smoke/short" target if it exists (e.g., `test:unit`, `test:smoke`, `make test-unit`). + 2. If the test runner supports "list tests / collect only / dry-run", prefer that (it validates wiring without executing). + 3. Otherwise run **one** small unit test file or **one** small test case using runner-specific filtering (choose the smallest/fastest-looking candidate). +* Runtime budget: + + * Keep the minimal test run under ~30s when possible. + * If you can't confidently keep it fast/offline, do **not** run it; mark ⏸️ and explain what you would run locally. +* Reporting: + + * In the validation log, explicitly label test validation as **"minimal test run"** and note how E2E/network/data mutation was avoided. + +### 4) Write or update the onboarding files + +Default: treat `AGENTS.md` as the canonical source. + +**Default file strategy (unless user requested only one target):** + +* Write/update `AGENTS.md` as the source of truth. +* Create/update `CLAUDE.md` as a **symlink to `AGENTS.md`** when feasible. + + * If symlinks are not supported in this environment/repo policy (or would be problematic cross-platform), fall back to keeping the files in sync by copying identical content. + * If you fall back to copying, include a brief note in the files (or in the report) explaining why a symlink was not used. + +Keep the files consistent with each other in either case. + +**CLAUDE.md consolidation workflow:** + +When CLAUDE.md exists as a standalone file (not already a symlink to AGENTS.md): + +1. **Detection**: Check if CLAUDE.md is a symlink (`ls -la CLAUDE.md` or equivalent). If it already points to AGENTS.md, no consolidation needed. + +2. **Interactive modes (quick-start, update, evaluate)**: Ask the user: + > "CLAUDE.md exists as a standalone file. Would you like to consolidate its content into AGENTS.md and replace CLAUDE.md with a symlink? This creates a single source of truth for agent documentation." + + Provide options: Yes (consolidate) / No (keep separate) / Show diff (preview what would be merged) + +3. **Auto mode**: Perform consolidation automatically without asking: + * Read both CLAUDE.md and AGENTS.md (if it exists) + * Merge content intelligently: deduplicate, prefer more accurate/recent content, reconcile contradictions + * Write the consolidated content to AGENTS.md + * Remove the standalone CLAUDE.md + * Create symlink: `ln -s AGENTS.md CLAUDE.md` + * Report exactly what content was merged/changed + +4. **Consolidation merge strategy**: + * If only CLAUDE.md exists: rename to AGENTS.md, create symlink + * If both exist: merge sections by category (WHY/WHAT/HOW), deduplicate commands, keep the most accurate/complete version of each section + * Preserve any CLAUDE.md-specific content that doesn't exist in AGENTS.md + * Note in the report which content came from which source + +If both exist but differ: + +* In update/evaluate/auto, reconcile into one canonical version (prefer the one that is more accurate). +* Remove contradictions; keep the final content consistent. + +Required structure (adapt headings as needed, but keep the intent): + +1. **Project overview (WHY)** + + * 1–3 paragraphs: what it is, who uses it, what "done" means. +2. **Repo map (WHAT)** + + * Bullet list of the few directories/packages that matter most. + * For monorepos: identify apps/services and shared packages. +3. **How to work here (HOW)** + + * Short workflow guidance (explore → plan → implement → validate). + * Explicit validation expectations (tests/typecheck/lint) with validated commands. +4. **Common commands (validated)** + + * Compact list with brief notes (where to run, prerequisites). +5. **Progressive disclosure pointers** + + * Links to authoritative docs or config files (paths). + * Reference any `ai_agent_docs/` files with brief descriptions so Claude can decide which to load. + * Proactively create `ai_agent_docs/` files for cross-cutting topics (architecture, conventions, testing philosophy, etc.) rather than bloating the root AGENTS.md. +6. **Gotchas / invariants** + + * Only high-impact, stable surprises that are not obvious from the code. + +Absolutely avoid: + +* long style guides (prefer "run formatter/linter X") +* huge command lists +* copy-pasted code that will go stale + * Prefer pointing to authoritative files (optionally with path:line references) over copying snippets that may go stale + +#### Progressive disclosure: Nested AGENTS.md vs ai_agent_docs/ + +Two complementary approaches exist for progressive disclosure. Use both as appropriate. + +**Nested AGENTS.md (module-specific docs)**: +* **Preferred for**: Module/service-specific documentation with distinct workflows +* **Auto-discovery**: Claude Code automatically discovers nested CLAUDE.md files +* **Location**: Colocated with the module (e.g., `/config/nvim/AGENTS.md`, `/services/api/AGENTS.md`) +* **Symlink requirement**: Always create `CLAUDE.md` as a symlink to `AGENTS.md` in the same directory (Claude Code loads CLAUDE.md, so the symlink ensures it picks up your AGENTS.md content) +* **Content**: Module-specific commands, workflows, gotchas +* **Structure**: Same WHY/WHAT/HOW format, but scoped to that module + +**ai_agent_docs/ (cross-cutting concerns)**: +* **Preferred for**: Topics that span multiple modules or are architectural in nature +* **Location**: Can exist at any scope level (root or nested within a module) +* **Example topics**: Architecture, conventions, testing philosophy, deployment patterns +* **Reference**: Include in the nearest AGENTS.md's progressive disclosure section + +**Example hierarchy**: +``` +AGENTS.md # Root overview (source of truth) +CLAUDE.md -> AGENTS.md # Symlink for Claude Code discovery +ai_agent_docs/ # Repo-wide cross-cutting +├── architecture.md +└── conventions.md +config/ +├── nvim/ +│ ├── AGENTS.md # nvim-specific (source of truth) +│ ├── CLAUDE.md -> AGENTS.md # Symlink for Claude Code +│ └── ai_agent_docs/ # nvim cross-cutting (if needed) +│ └── plugin-patterns.md +└── zsh/ + ├── AGENTS.md # zsh-specific (source of truth) + └── CLAUDE.md -> AGENTS.md # Symlink for Claude Code +``` + +**Decision guidance**: +* Module has distinct workflows/commands → create nested `AGENTS.md` +* Topic spans modules or is architectural → use `ai_agent_docs/` +* Keep both concise; prefer colocated AGENTS.md when in doubt + +**When to create**: +* **Auto mode**: Proactively create nested AGENTS.md when processing multiple scopes; create ai_agent_docs/ for cross-cutting concerns. Always create CLAUDE.md symlinks alongside each AGENTS.md. +* **Interactive modes**: Suggest based on complexity; ask user which approach fits their mental model + +**Content guidelines** (applies to both): +* Keep each file focused and concise (target <100 lines for ai_agent_docs/, <150 for nested AGENTS.md) +* Use `file:line` references instead of copying code +* Each file should be self-contained for its topic +* Include the same validation markers (✅/⚠️/⏸️) for any commands + +**Validation**: Same rules as main docs—validate commands, confirm paths exist, mark execution status. + +**Referencing in AGENTS.md**: Include a "Task-Specific Docs" section: +```markdown +## Task-Specific Docs + +Nested module docs (auto-discovered by Claude Code): +- `config/nvim/AGENTS.md` - Neovim configuration +- `config/zsh/AGENTS.md` - Zsh shell setup + +Cross-cutting docs in `ai_agent_docs/`: +- `ai_agent_docs/architecture.md` - System design and key abstractions +- `ai_agent_docs/conventions.md` - Code style and patterns +``` + +This allows Claude to selectively load only the context needed for the current task. + +### 5) Evaluate (evaluate / auto) + +Provide a short rubric report (in chat), covering: + +* Conciseness and universality (what was removed or moved out) +* Correctness (what was validated, what was stale) +* Progressive disclosure quality +* Monorepo clarity +* AGENTS vs CLAUDE consistency + +In auto mode: apply improvements directly. + +### 6) Report back (all modes) + +In chat, output: + +* Files created/updated (paths) +* Key changes (bullets) +* Validation log (commands + ✅ / ⚠️ / ⏸️) +* **Symlink status**: For each AGENTS.md created/updated, report whether CLAUDE.md symlink exists: + * ✅ `path/CLAUDE.md -> AGENTS.md` (symlink exists) + * ⚠️ `path/CLAUDE.md` missing symlink (created it / needs manual creation) + * Remind user: "AGENTS.md is the source of truth. Claude Code discovers CLAUDE.md files, so the symlink ensures your docs are loaded." +* Remaining unknowns or follow-ups (if any) + +Do **NOT** paste full file contents unless the user asks. Summarize and point to the files. + +--- + +## Now execute + +Proceed in the parsed mode, using the scope rules above. + +This command is designed to keep AGENTS.md and CLAUDE.md short, accurate, and universally useful, while enforcing **"validate what you write"** with concrete, safe, and minimal command execution. diff --git a/src/__init__.py b/src/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/example.py b/src/example.py deleted file mode 100644 index 1d82bcd..0000000 --- a/src/example.py +++ /dev/null @@ -1,35 +0,0 @@ -import statistics -from dataclasses import dataclass -from datetime import datetime -from typing import Optional - - -@dataclass -class DataPoint: - """Example data structure for time series data.""" - - timestamp: datetime - value: float - label: Optional[str] = None - - -def calculate_moving_average( - data: list[DataPoint], window_size: int = 3 -) -> list[float]: - """Calculate moving average of values. - - Args: - data: List of DataPoint objects - window_size: Size of moving window - - Returns: - List of moving averages - """ - values = [d.value for d in data] - result = [] - - for i in range(len(values)): - window = values[max(0, i - window_size + 1) : i + 1] - result.append(statistics.mean(window)) - - return result diff --git a/templates/.github/workflows/docs.yml.template b/templates/.github/workflows/docs.yml.template deleted file mode 100644 index 2668105..0000000 --- a/templates/.github/workflows/docs.yml.template +++ /dev/null @@ -1,82 +0,0 @@ -name: Deploy Documentation - -on: - push: - branches: - - main - paths: - - 'docs/**' - - 'mkdocs.yml' - - '.github/workflows/docs.yml' - pull_request: - branches: - - main - paths: - - 'docs/**' - - 'mkdocs.yml' - -permissions: - contents: write - -jobs: - deploy: - runs-on: ubuntu-latest - if: github.event_name == 'push' - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Configure Git Credentials - run: | - git config user.name github-actions[bot] - git config user.email 41898282+github-actions[bot]@users.noreply.github.com - - - uses: actions/setup-python@v5 - with: - python-version: 3.x - - - name: Install uv - run: pip install uv - - - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV - - - uses: actions/cache@v4 - with: - key: mkdocs-material-${{ env.cache_id }} - path: .cache - restore-keys: | - mkdocs-material- - - - name: Install dependencies - run: uv sync --group dev - - - name: Deploy documentation - run: uv run mkdocs gh-deploy --force - - build-check: - runs-on: ubuntu-latest - if: github.event_name == 'pull_request' - - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-python@v5 - with: - python-version: 3.x - - - name: Install uv - run: pip install uv - - - name: Install dependencies - run: uv sync --group dev - - - name: Build documentation - run: uv run mkdocs build --strict - - - name: Check build output - run: | - echo "✅ Documentation builds successfully" - echo "📊 Site size: $(du -sh site/ | cut -f1)" - echo "📄 Pages built: $(find site/ -name "*.html" | wc -l)" \ No newline at end of file diff --git a/templates/docs/getting-started.md.template b/templates/docs/getting-started.md.template deleted file mode 100644 index 78f1534..0000000 --- a/templates/docs/getting-started.md.template +++ /dev/null @@ -1,100 +0,0 @@ -# Getting Started - -This guide will help you get started with {project_name}. - -## Prerequisites - -- Python 3.9 or higher -- Git - -## Installation - -### End Users - -Install from PyPI: -```bash -pip install {project_name} -``` - -### Developers - -1. Clone the repository: - ```bash - git clone https://github.com/{github_username}/{project_name}.git - cd {project_name} - ``` - -2. Set up the development environment: - ```bash - make setup - ``` - -3. Run the tests to verify everything works: - ```bash - make test - ``` - -## Basic Usage - -```python -import {project_module_name} - -# Add your basic usage examples here -``` - -## Development Workflow - -1. Make your changes to the code -2. Add or update tests as needed -3. Run quality checks: - ```bash - make check - ``` -4. Update documentation if needed -5. Commit your changes -6. Create a pull request - -## Available Commands - -Run `make` to see all available commands: - -- `make setup` - Set up development environment -- `make test` - Run tests with coverage -- `make lint` - Run linting -- `make format` - Format code -- `make ty` - Run type checking -- `make check` - Run all quality checks -- `make docs-serve` - Serve documentation locally -- `make docs-build` - Build documentation - -## Testing - -Run the test suite: -```bash -make test -``` - -Run specific tests: -```bash -uv run -m pytest tests/test_specific.py::test_function_name -``` - -## Documentation - -### Viewing Documentation - -Serve documentation locally: -```bash -make docs-serve -``` - -The documentation will be available at http://localhost:8000 - -### Building Documentation - -Build static documentation: -```bash -make docs-build -``` - -The built documentation will be in the `site/` directory. \ No newline at end of file diff --git a/templates/docs/index.md.template b/templates/docs/index.md.template deleted file mode 100644 index 9fd2f92..0000000 --- a/templates/docs/index.md.template +++ /dev/null @@ -1,85 +0,0 @@ -# {project_name} - -{project_description} - -## Features - -- 🔧 Modern Python tooling with UV package manager -- 🧪 Comprehensive testing with pytest -- 🎨 Code formatting with Ruff -- 🔍 Type checking with MyPy -- 📚 Documentation with MkDocs + Material -- 🚀 CI/CD with GitHub Actions -- 🐳 Docker support for development - -## Quick Start - -```bash -# Clone the repository -git clone https://github.com/{github_username}/{project_name}.git -cd {project_name} - -# Set up the development environment -make setup - -# Run quality checks -make check -``` - -## Installation - -### For Users - -```bash -pip install {project_name} -``` - -### For Development - -```bash -# Clone the repository -git clone https://github.com/{github_username}/{project_name}.git -cd {project_name} - -# Set up development environment -make setup - -# Install pre-commit hooks (optional) -make install-hooks -``` - -## Project Structure - -``` -{project_name}/ -├── {project_module_name}/ # Main package -├── tests/ # Test files -├── docs/ # Documentation -├── scripts/ # Utility scripts -├── docker/ # Docker configuration -└── .github/workflows/ # CI/CD automation -``` - -## Usage - -```python -import {project_module_name} - -# Your usage examples here -``` - -## Development - -See the [Getting Started](getting-started.md) guide for detailed development instructions. - -## Contributing - -1. Fork the repository -2. Create a feature branch -3. Make your changes -4. Run the test suite: `make check` -5. Submit a pull request - -## License - -This project is licensed under the MIT License - see the LICENSE file for details. \ No newline at end of file diff --git a/templates/docs/reference/api.md.template b/templates/docs/reference/api.md.template deleted file mode 100644 index 411771e..0000000 --- a/templates/docs/reference/api.md.template +++ /dev/null @@ -1,9 +0,0 @@ -# API Reference - -This page contains the auto-generated API documentation for {project_name}. - -::: {project_module_name} - options: - show_root_heading: true - members_order: source - show_source: false \ No newline at end of file diff --git a/templates/mkdocs.yml.template b/templates/mkdocs.yml.template deleted file mode 100644 index aceb7cc..0000000 --- a/templates/mkdocs.yml.template +++ /dev/null @@ -1,80 +0,0 @@ -site_name: {project_name} -site_url: https://{github_username}.github.io/{project_name}/ -site_description: {project_description} -site_author: {author_name} - -repo_name: {github_username}/{project_name} -repo_url: https://github.com/{github_username}/{project_name} -edit_uri: edit/main/docs/ - -theme: - name: material - features: - - navigation.tabs - - navigation.tabs.sticky - - navigation.sections - - navigation.top - - navigation.tracking - - search.suggest - - search.highlight - - search.share - - toc.follow - - content.action.edit - - content.code.copy - - content.code.annotate - palette: - - scheme: default - primary: indigo - accent: indigo - toggle: - icon: material/brightness-7 - name: Switch to dark mode - - scheme: slate - primary: indigo - accent: indigo - toggle: - icon: material/brightness-4 - name: Switch to light mode - font: - text: Roboto - code: Roboto Mono - icon: - logo: material/library - -plugins: - - search - - mkdocstrings: - handlers: - python: - paths: [{project_module_name}] - options: - show_source: true - show_root_heading: true - merge_init_into_class: true - docstring_style: google - show_signature_annotations: true - separate_signature: true - -markdown_extensions: - - admonition - - attr_list - - pymdownx.details - - pymdownx.superfences - - pymdownx.tabbed: - alternate_style: true - - pymdownx.highlight: - anchor_linenums: true - - pymdownx.inlinehilite - - toc: - permalink: true - -nav: - - Home: index.md - - Getting Started: getting-started.md - - Reference: - - API Documentation: reference/api.md - -extra: - social: - - icon: fontawesome/brands/github - link: https://github.com/{github_username}/{project_name} \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_docs_setup.py b/tests/test_docs_setup.py deleted file mode 100644 index 71d70ae..0000000 --- a/tests/test_docs_setup.py +++ /dev/null @@ -1,136 +0,0 @@ -"""Test MkDocs documentation setup and configuration.""" - -import subprocess -import tomli -from pathlib import Path - - -def test_mkdocs_dependencies_configured(): - """Test that MkDocs dependencies are configured in pyproject.toml.""" - pyproject_path = Path("pyproject.toml") - assert pyproject_path.exists(), "pyproject.toml should exist" - - with open(pyproject_path, "rb") as f: - config = tomli.load(f) - - # Check for dependency groups with docs dependencies (now conditional) - if "dependency-groups" in config and "dev" in config["dependency-groups"]: - dev_deps = config["dependency-groups"]["dev"] - - # Check for MkDocs dependencies - mkdocs_deps = [dep for dep in dev_deps if "mkdocs" in dep.lower()] - - if mkdocs_deps: # Only check if docs deps are present - assert len(mkdocs_deps) >= 2, "Should have mkdocs-material and mkdocstrings dependencies" - - # Verify specific dependencies - has_material = any("mkdocs-material" in dep for dep in dev_deps) - has_mkdocstrings = any("mkdocstrings" in dep for dep in dev_deps) - - assert has_material, "Should have mkdocs-material dependency" - assert has_mkdocstrings, "Should have mkdocstrings dependency" - - -def test_mkdocs_config_exists(): - """Test that mkdocs.yml configuration file exists (if docs enabled).""" - mkdocs_config = Path("mkdocs.yml") - if mkdocs_config.exists(): - # If it exists, it should be valid - assert mkdocs_config.is_file(), "mkdocs.yml should be a file" - - -def test_mkdocs_config_valid(): - """Test that mkdocs.yml has valid configuration (if it exists).""" - mkdocs_config = Path("mkdocs.yml") - if not mkdocs_config.exists(): - return # Skip if docs not enabled - - import yaml - - with open(mkdocs_config, "r") as f: - config = yaml.safe_load(f) - - # Check required fields - assert "site_name" in config, "mkdocs.yml should have site_name" - assert "theme" in config, "mkdocs.yml should have theme configuration" - assert config["theme"]["name"] == "material", "Should use Material theme" - assert "plugins" in config, "mkdocs.yml should have plugins" - - # Check for required plugins - plugin_names = [] - for plugin in config["plugins"]: - if isinstance(plugin, dict): - plugin_names.extend(plugin.keys()) - else: - plugin_names.append(plugin) - - assert "search" in plugin_names, "Should have search plugin" - assert "mkdocstrings" in plugin_names, "Should have mkdocstrings plugin" - - -def test_documentation_structure_exists(): - """Test that basic documentation structure exists (if docs enabled).""" - docs_dir = Path("docs") - if not docs_dir.exists(): - return # Skip if docs not enabled - - assert docs_dir.is_dir(), "docs should be a directory" - - # Check for essential documentation files - index_file = docs_dir / "index.md" - assert index_file.exists(), "docs/index.md should exist" - - -def test_makefile_has_docs_targets(): - """Test that Makefile contains documentation targets.""" - makefile_path = Path("Makefile") - assert makefile_path.exists(), "Makefile should exist" - - with open(makefile_path, "r") as f: - makefile_content = f.read() - - # Check for documentation targets - assert "docs-install:" in makefile_content, "Makefile should have docs-install target" - assert "docs-build:" in makefile_content, "Makefile should have docs-build target" - assert "docs-serve:" in makefile_content, "Makefile should have docs-serve target" - assert "docs-check:" in makefile_content, "Makefile should have docs-check target" - assert "docs-clean:" in makefile_content, "Makefile should have docs-clean target" - - -def test_github_actions_docs_workflow(): - """Test that GitHub Actions workflow for docs exists (if docs enabled).""" - workflow_path = Path(".github/workflows/docs.yml") - if not workflow_path.exists(): - return # Skip if docs not enabled - - import yaml - with open(workflow_path, "r") as f: - workflow = yaml.safe_load(f) - - # Note: YAML parses "on" as boolean True, not string "on" - assert True in workflow or "on" in workflow, "Workflow should have trigger configuration" - assert "jobs" in workflow, "Workflow should have jobs" - - # Check for deployment job - jobs = workflow["jobs"] - assert any("deploy" in job_name.lower() or "docs" in job_name.lower() - for job_name in jobs.keys()), "Should have a docs deployment job" - - -def test_template_files_exist(): - """Test that template files exist for documentation setup.""" - template_dir = Path("templates") - assert template_dir.exists(), "templates/ directory should exist" - - # Check for essential template files - template_files = [ - "mkdocs.yml.template", - "docs/index.md.template", - "docs/getting-started.md.template", - "docs/reference/api.md.template", - ".github/workflows/docs.yml.template" - ] - - for template_file in template_files: - template_path = template_dir / template_file - assert template_path.exists(), f"Template file {template_file} should exist" \ No newline at end of file diff --git a/tests/test_example.py b/tests/test_example.py deleted file mode 100644 index 62a0da6..0000000 --- a/tests/test_example.py +++ /dev/null @@ -1,13 +0,0 @@ -from datetime import datetime -from src.example import DataPoint, calculate_moving_average - -def test_moving_average(): - data = [ - DataPoint(datetime(2024, 1, 1), 1.0, "A"), - DataPoint(datetime(2024, 1, 2), 2.0, "B"), - DataPoint(datetime(2024, 1, 3), 3.0, "C"), - DataPoint(datetime(2024, 1, 4), 4.0, "D"), - ] - - result = calculate_moving_average(data, window_size=2) - assert result == [1.0, 1.5, 2.5, 3.5] diff --git a/uv.lock b/uv.lock deleted file mode 100644 index c21e272..0000000 --- a/uv.lock +++ /dev/null @@ -1,998 +0,0 @@ -version = 1 -revision = 2 -requires-python = ">=3.9" -resolution-markers = [ - "python_full_version >= '3.10'", - "python_full_version < '3.10'", -] - -[[package]] -name = "babel" -version = "2.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, -] - -[[package]] -name = "backrefs" -version = "5.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/46/caba1eb32fa5784428ab401a5487f73db4104590ecd939ed9daaf18b47e0/backrefs-5.8.tar.gz", hash = "sha256:2cab642a205ce966af3dd4b38ee36009b31fa9502a35fd61d59ccc116e40a6bd", size = 6773994, upload-time = "2025-02-25T18:15:32.003Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/cb/d019ab87fe70e0fe3946196d50d6a4428623dc0c38a6669c8cae0320fbf3/backrefs-5.8-py310-none-any.whl", hash = "sha256:c67f6638a34a5b8730812f5101376f9d41dc38c43f1fdc35cb54700f6ed4465d", size = 380337, upload-time = "2025-02-25T16:53:14.607Z" }, - { url = "https://files.pythonhosted.org/packages/a9/86/abd17f50ee21b2248075cb6924c6e7f9d23b4925ca64ec660e869c2633f1/backrefs-5.8-py311-none-any.whl", hash = "sha256:2e1c15e4af0e12e45c8701bd5da0902d326b2e200cafcd25e49d9f06d44bb61b", size = 392142, upload-time = "2025-02-25T16:53:17.266Z" }, - { url = "https://files.pythonhosted.org/packages/b3/04/7b415bd75c8ab3268cc138c76fa648c19495fcc7d155508a0e62f3f82308/backrefs-5.8-py312-none-any.whl", hash = "sha256:bbef7169a33811080d67cdf1538c8289f76f0942ff971222a16034da88a73486", size = 398021, upload-time = "2025-02-25T16:53:26.378Z" }, - { url = "https://files.pythonhosted.org/packages/04/b8/60dcfb90eb03a06e883a92abbc2ab95c71f0d8c9dd0af76ab1d5ce0b1402/backrefs-5.8-py313-none-any.whl", hash = "sha256:e3a63b073867dbefd0536425f43db618578528e3896fb77be7141328642a1585", size = 399915, upload-time = "2025-02-25T16:53:28.167Z" }, - { url = "https://files.pythonhosted.org/packages/0c/37/fb6973edeb700f6e3d6ff222400602ab1830446c25c7b4676d8de93e65b8/backrefs-5.8-py39-none-any.whl", hash = "sha256:a66851e4533fb5b371aa0628e1fee1af05135616b86140c9d787a2ffdf4b8fdc", size = 380336, upload-time = "2025-02-25T16:53:29.858Z" }, -] - -[[package]] -name = "certifi" -version = "2025.6.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/73/f7/f14b46d4bcd21092d7d3ccef689615220d8a08fb25e564b65d20738e672e/certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b", size = 158753, upload-time = "2025-06-15T02:45:51.329Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/ae/320161bd181fc06471eed047ecce67b693fd7515b16d495d8932db763426/certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057", size = 157650, upload-time = "2025-06-15T02:45:49.977Z" }, -] - -[[package]] -name = "cfgv" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818, upload-time = "2025-05-02T08:31:46.725Z" }, - { url = "https://files.pythonhosted.org/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649, upload-time = "2025-05-02T08:31:48.889Z" }, - { url = "https://files.pythonhosted.org/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045, upload-time = "2025-05-02T08:31:50.757Z" }, - { url = "https://files.pythonhosted.org/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356, upload-time = "2025-05-02T08:31:52.634Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471, upload-time = "2025-05-02T08:31:56.207Z" }, - { url = "https://files.pythonhosted.org/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317, upload-time = "2025-05-02T08:31:57.613Z" }, - { url = "https://files.pythonhosted.org/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368, upload-time = "2025-05-02T08:31:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491, upload-time = "2025-05-02T08:32:01.219Z" }, - { url = "https://files.pythonhosted.org/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695, upload-time = "2025-05-02T08:32:03.045Z" }, - { url = "https://files.pythonhosted.org/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849, upload-time = "2025-05-02T08:32:04.651Z" }, - { url = "https://files.pythonhosted.org/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091, upload-time = "2025-05-02T08:32:06.719Z" }, - { url = "https://files.pythonhosted.org/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445, upload-time = "2025-05-02T08:32:08.66Z" }, - { url = "https://files.pythonhosted.org/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782, upload-time = "2025-05-02T08:32:10.46Z" }, - { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" }, - { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" }, - { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" }, - { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" }, - { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" }, - { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" }, - { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" }, - { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" }, - { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" }, - { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" }, - { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" }, - { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" }, - { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, - { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, - { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, - { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, - { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, - { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, - { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, - { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, - { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, - { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, - { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, - { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, - { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, - { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, - { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, - { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, - { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, - { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, - { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, - { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, - { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, - { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, - { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, - { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, - { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, - { url = "https://files.pythonhosted.org/packages/28/f8/dfb01ff6cc9af38552c69c9027501ff5a5117c4cc18dcd27cb5259fa1888/charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4", size = 201671, upload-time = "2025-05-02T08:34:12.696Z" }, - { url = "https://files.pythonhosted.org/packages/32/fb/74e26ee556a9dbfe3bd264289b67be1e6d616329403036f6507bb9f3f29c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7", size = 144744, upload-time = "2025-05-02T08:34:14.665Z" }, - { url = "https://files.pythonhosted.org/packages/ad/06/8499ee5aa7addc6f6d72e068691826ff093329fe59891e83b092ae4c851c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836", size = 154993, upload-time = "2025-05-02T08:34:17.134Z" }, - { url = "https://files.pythonhosted.org/packages/f1/a2/5e4c187680728219254ef107a6949c60ee0e9a916a5dadb148c7ae82459c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597", size = 147382, upload-time = "2025-05-02T08:34:19.081Z" }, - { url = "https://files.pythonhosted.org/packages/4c/fe/56aca740dda674f0cc1ba1418c4d84534be51f639b5f98f538b332dc9a95/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7", size = 149536, upload-time = "2025-05-02T08:34:21.073Z" }, - { url = "https://files.pythonhosted.org/packages/53/13/db2e7779f892386b589173dd689c1b1e304621c5792046edd8a978cbf9e0/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f", size = 151349, upload-time = "2025-05-02T08:34:23.193Z" }, - { url = "https://files.pythonhosted.org/packages/69/35/e52ab9a276186f729bce7a0638585d2982f50402046e4b0faa5d2c3ef2da/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba", size = 146365, upload-time = "2025-05-02T08:34:25.187Z" }, - { url = "https://files.pythonhosted.org/packages/a6/d8/af7333f732fc2e7635867d56cb7c349c28c7094910c72267586947561b4b/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12", size = 154499, upload-time = "2025-05-02T08:34:27.359Z" }, - { url = "https://files.pythonhosted.org/packages/7a/3d/a5b2e48acef264d71e036ff30bcc49e51bde80219bb628ba3e00cf59baac/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518", size = 157735, upload-time = "2025-05-02T08:34:29.798Z" }, - { url = "https://files.pythonhosted.org/packages/85/d8/23e2c112532a29f3eef374375a8684a4f3b8e784f62b01da931186f43494/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5", size = 154786, upload-time = "2025-05-02T08:34:31.858Z" }, - { url = "https://files.pythonhosted.org/packages/c7/57/93e0169f08ecc20fe82d12254a200dfaceddc1c12a4077bf454ecc597e33/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3", size = 150203, upload-time = "2025-05-02T08:34:33.88Z" }, - { url = "https://files.pythonhosted.org/packages/2c/9d/9bf2b005138e7e060d7ebdec7503d0ef3240141587651f4b445bdf7286c2/charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471", size = 98436, upload-time = "2025-05-02T08:34:35.907Z" }, - { url = "https://files.pythonhosted.org/packages/6d/24/5849d46cf4311bbf21b424c443b09b459f5b436b1558c04e45dbb7cc478b/charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e", size = 105772, upload-time = "2025-05-02T08:34:37.935Z" }, - { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, -] - -[[package]] -name = "click" -version = "8.1.8" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, -] - -[[package]] -name = "click" -version = "8.2.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, -] - -[[package]] -name = "coverage" -version = "7.9.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/e0/98670a80884f64578f0c22cd70c5e81a6e07b08167721c7487b4d70a7ca0/coverage-7.9.1.tar.gz", hash = "sha256:6cf43c78c4282708a28e466316935ec7489a9c487518a77fa68f716c67909cec", size = 813650, upload-time = "2025-06-13T13:02:28.627Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/78/1c1c5ec58f16817c09cbacb39783c3655d54a221b6552f47ff5ac9297603/coverage-7.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cc94d7c5e8423920787c33d811c0be67b7be83c705f001f7180c7b186dcf10ca", size = 212028, upload-time = "2025-06-13T13:00:29.293Z" }, - { url = "https://files.pythonhosted.org/packages/98/db/e91b9076f3a888e3b4ad7972ea3842297a52cc52e73fd1e529856e473510/coverage-7.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16aa0830d0c08a2c40c264cef801db8bc4fc0e1892782e45bcacbd5889270509", size = 212420, upload-time = "2025-06-13T13:00:34.027Z" }, - { url = "https://files.pythonhosted.org/packages/0e/d0/2b3733412954576b0aea0a16c3b6b8fbe95eb975d8bfa10b07359ead4252/coverage-7.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf95981b126f23db63e9dbe4cf65bd71f9a6305696fa5e2262693bc4e2183f5b", size = 241529, upload-time = "2025-06-13T13:00:35.786Z" }, - { url = "https://files.pythonhosted.org/packages/b3/00/5e2e5ae2e750a872226a68e984d4d3f3563cb01d1afb449a17aa819bc2c4/coverage-7.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f05031cf21699785cd47cb7485f67df619e7bcdae38e0fde40d23d3d0210d3c3", size = 239403, upload-time = "2025-06-13T13:00:37.399Z" }, - { url = "https://files.pythonhosted.org/packages/37/3b/a2c27736035156b0a7c20683afe7df498480c0dfdf503b8c878a21b6d7fb/coverage-7.9.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb4fbcab8764dc072cb651a4bcda4d11fb5658a1d8d68842a862a6610bd8cfa3", size = 240548, upload-time = "2025-06-13T13:00:39.647Z" }, - { url = "https://files.pythonhosted.org/packages/98/f5/13d5fc074c3c0e0dc80422d9535814abf190f1254d7c3451590dc4f8b18c/coverage-7.9.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0f16649a7330ec307942ed27d06ee7e7a38417144620bb3d6e9a18ded8a2d3e5", size = 240459, upload-time = "2025-06-13T13:00:40.934Z" }, - { url = "https://files.pythonhosted.org/packages/36/24/24b9676ea06102df824c4a56ffd13dc9da7904478db519efa877d16527d5/coverage-7.9.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:cea0a27a89e6432705fffc178064503508e3c0184b4f061700e771a09de58187", size = 239128, upload-time = "2025-06-13T13:00:42.343Z" }, - { url = "https://files.pythonhosted.org/packages/be/05/242b7a7d491b369ac5fee7908a6e5ba42b3030450f3ad62c645b40c23e0e/coverage-7.9.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e980b53a959fa53b6f05343afbd1e6f44a23ed6c23c4b4c56c6662bbb40c82ce", size = 239402, upload-time = "2025-06-13T13:00:43.634Z" }, - { url = "https://files.pythonhosted.org/packages/73/e0/4de7f87192fa65c9c8fbaeb75507e124f82396b71de1797da5602898be32/coverage-7.9.1-cp310-cp310-win32.whl", hash = "sha256:70760b4c5560be6ca70d11f8988ee6542b003f982b32f83d5ac0b72476607b70", size = 214518, upload-time = "2025-06-13T13:00:45.622Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ab/5e4e2fe458907d2a65fab62c773671cfc5ac704f1e7a9ddd91996f66e3c2/coverage-7.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:a66e8f628b71f78c0e0342003d53b53101ba4e00ea8dabb799d9dba0abbbcebe", size = 215436, upload-time = "2025-06-13T13:00:47.245Z" }, - { url = "https://files.pythonhosted.org/packages/60/34/fa69372a07d0903a78ac103422ad34db72281c9fc625eba94ac1185da66f/coverage-7.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:95c765060e65c692da2d2f51a9499c5e9f5cf5453aeaf1420e3fc847cc060582", size = 212146, upload-time = "2025-06-13T13:00:48.496Z" }, - { url = "https://files.pythonhosted.org/packages/27/f0/da1894915d2767f093f081c42afeba18e760f12fdd7a2f4acbe00564d767/coverage-7.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ba383dc6afd5ec5b7a0d0c23d38895db0e15bcba7fb0fa8901f245267ac30d86", size = 212536, upload-time = "2025-06-13T13:00:51.535Z" }, - { url = "https://files.pythonhosted.org/packages/10/d5/3fc33b06e41e390f88eef111226a24e4504d216ab8e5d1a7089aa5a3c87a/coverage-7.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37ae0383f13cbdcf1e5e7014489b0d71cc0106458878ccde52e8a12ced4298ed", size = 245092, upload-time = "2025-06-13T13:00:52.883Z" }, - { url = "https://files.pythonhosted.org/packages/0a/39/7aa901c14977aba637b78e95800edf77f29f5a380d29768c5b66f258305b/coverage-7.9.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69aa417a030bf11ec46149636314c24c8d60fadb12fc0ee8f10fda0d918c879d", size = 242806, upload-time = "2025-06-13T13:00:54.571Z" }, - { url = "https://files.pythonhosted.org/packages/43/fc/30e5cfeaf560b1fc1989227adedc11019ce4bb7cce59d65db34fe0c2d963/coverage-7.9.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a4be2a28656afe279b34d4f91c3e26eccf2f85500d4a4ff0b1f8b54bf807338", size = 244610, upload-time = "2025-06-13T13:00:56.932Z" }, - { url = "https://files.pythonhosted.org/packages/bf/15/cca62b13f39650bc87b2b92bb03bce7f0e79dd0bf2c7529e9fc7393e4d60/coverage-7.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:382e7ddd5289f140259b610e5f5c58f713d025cb2f66d0eb17e68d0a94278875", size = 244257, upload-time = "2025-06-13T13:00:58.545Z" }, - { url = "https://files.pythonhosted.org/packages/cd/1a/c0f2abe92c29e1464dbd0ff9d56cb6c88ae2b9e21becdb38bea31fcb2f6c/coverage-7.9.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e5532482344186c543c37bfad0ee6069e8ae4fc38d073b8bc836fc8f03c9e250", size = 242309, upload-time = "2025-06-13T13:00:59.836Z" }, - { url = "https://files.pythonhosted.org/packages/57/8d/c6fd70848bd9bf88fa90df2af5636589a8126d2170f3aade21ed53f2b67a/coverage-7.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a39d18b3f50cc121d0ce3838d32d58bd1d15dab89c910358ebefc3665712256c", size = 242898, upload-time = "2025-06-13T13:01:02.506Z" }, - { url = "https://files.pythonhosted.org/packages/c2/9e/6ca46c7bff4675f09a66fe2797cd1ad6a24f14c9c7c3b3ebe0470a6e30b8/coverage-7.9.1-cp311-cp311-win32.whl", hash = "sha256:dd24bd8d77c98557880def750782df77ab2b6885a18483dc8588792247174b32", size = 214561, upload-time = "2025-06-13T13:01:04.012Z" }, - { url = "https://files.pythonhosted.org/packages/a1/30/166978c6302010742dabcdc425fa0f938fa5a800908e39aff37a7a876a13/coverage-7.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:6b55ad10a35a21b8015eabddc9ba31eb590f54adc9cd39bcf09ff5349fd52125", size = 215493, upload-time = "2025-06-13T13:01:05.702Z" }, - { url = "https://files.pythonhosted.org/packages/60/07/a6d2342cd80a5be9f0eeab115bc5ebb3917b4a64c2953534273cf9bc7ae6/coverage-7.9.1-cp311-cp311-win_arm64.whl", hash = "sha256:6ad935f0016be24c0e97fc8c40c465f9c4b85cbbe6eac48934c0dc4d2568321e", size = 213869, upload-time = "2025-06-13T13:01:09.345Z" }, - { url = "https://files.pythonhosted.org/packages/68/d9/7f66eb0a8f2fce222de7bdc2046ec41cb31fe33fb55a330037833fb88afc/coverage-7.9.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8de12b4b87c20de895f10567639c0797b621b22897b0af3ce4b4e204a743626", size = 212336, upload-time = "2025-06-13T13:01:10.909Z" }, - { url = "https://files.pythonhosted.org/packages/20/20/e07cb920ef3addf20f052ee3d54906e57407b6aeee3227a9c91eea38a665/coverage-7.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5add197315a054e92cee1b5f686a2bcba60c4c3e66ee3de77ace6c867bdee7cb", size = 212571, upload-time = "2025-06-13T13:01:12.518Z" }, - { url = "https://files.pythonhosted.org/packages/78/f8/96f155de7e9e248ca9c8ff1a40a521d944ba48bec65352da9be2463745bf/coverage-7.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:600a1d4106fe66f41e5d0136dfbc68fe7200a5cbe85610ddf094f8f22e1b0300", size = 246377, upload-time = "2025-06-13T13:01:14.87Z" }, - { url = "https://files.pythonhosted.org/packages/3e/cf/1d783bd05b7bca5c10ded5f946068909372e94615a4416afadfe3f63492d/coverage-7.9.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a876e4c3e5a2a1715a6608906aa5a2e0475b9c0f68343c2ada98110512ab1d8", size = 243394, upload-time = "2025-06-13T13:01:16.23Z" }, - { url = "https://files.pythonhosted.org/packages/02/dd/e7b20afd35b0a1abea09fb3998e1abc9f9bd953bee548f235aebd2b11401/coverage-7.9.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81f34346dd63010453922c8e628a52ea2d2ccd73cb2487f7700ac531b247c8a5", size = 245586, upload-time = "2025-06-13T13:01:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/4e/38/b30b0006fea9d617d1cb8e43b1bc9a96af11eff42b87eb8c716cf4d37469/coverage-7.9.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:888f8eee13f2377ce86d44f338968eedec3291876b0b8a7289247ba52cb984cd", size = 245396, upload-time = "2025-06-13T13:01:19.164Z" }, - { url = "https://files.pythonhosted.org/packages/31/e4/4d8ec1dc826e16791f3daf1b50943e8e7e1eb70e8efa7abb03936ff48418/coverage-7.9.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9969ef1e69b8c8e1e70d591f91bbc37fc9a3621e447525d1602801a24ceda898", size = 243577, upload-time = "2025-06-13T13:01:22.433Z" }, - { url = "https://files.pythonhosted.org/packages/25/f4/b0e96c5c38e6e40ef465c4bc7f138863e2909c00e54a331da335faf0d81a/coverage-7.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:60c458224331ee3f1a5b472773e4a085cc27a86a0b48205409d364272d67140d", size = 244809, upload-time = "2025-06-13T13:01:24.143Z" }, - { url = "https://files.pythonhosted.org/packages/8a/65/27e0a1fa5e2e5079bdca4521be2f5dabf516f94e29a0defed35ac2382eb2/coverage-7.9.1-cp312-cp312-win32.whl", hash = "sha256:5f646a99a8c2b3ff4c6a6e081f78fad0dde275cd59f8f49dc4eab2e394332e74", size = 214724, upload-time = "2025-06-13T13:01:25.435Z" }, - { url = "https://files.pythonhosted.org/packages/9b/a8/d5b128633fd1a5e0401a4160d02fa15986209a9e47717174f99dc2f7166d/coverage-7.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:30f445f85c353090b83e552dcbbdad3ec84c7967e108c3ae54556ca69955563e", size = 215535, upload-time = "2025-06-13T13:01:27.861Z" }, - { url = "https://files.pythonhosted.org/packages/a3/37/84bba9d2afabc3611f3e4325ee2c6a47cd449b580d4a606b240ce5a6f9bf/coverage-7.9.1-cp312-cp312-win_arm64.whl", hash = "sha256:af41da5dca398d3474129c58cb2b106a5d93bbb196be0d307ac82311ca234342", size = 213904, upload-time = "2025-06-13T13:01:29.202Z" }, - { url = "https://files.pythonhosted.org/packages/d0/a7/a027970c991ca90f24e968999f7d509332daf6b8c3533d68633930aaebac/coverage-7.9.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:31324f18d5969feef7344a932c32428a2d1a3e50b15a6404e97cba1cc9b2c631", size = 212358, upload-time = "2025-06-13T13:01:30.909Z" }, - { url = "https://files.pythonhosted.org/packages/f2/48/6aaed3651ae83b231556750280682528fea8ac7f1232834573472d83e459/coverage-7.9.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0c804506d624e8a20fb3108764c52e0eef664e29d21692afa375e0dd98dc384f", size = 212620, upload-time = "2025-06-13T13:01:32.256Z" }, - { url = "https://files.pythonhosted.org/packages/6c/2a/f4b613f3b44d8b9f144847c89151992b2b6b79cbc506dee89ad0c35f209d/coverage-7.9.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef64c27bc40189f36fcc50c3fb8f16ccda73b6a0b80d9bd6e6ce4cffcd810bbd", size = 245788, upload-time = "2025-06-13T13:01:33.948Z" }, - { url = "https://files.pythonhosted.org/packages/04/d2/de4fdc03af5e4e035ef420ed26a703c6ad3d7a07aff2e959eb84e3b19ca8/coverage-7.9.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4fe2348cc6ec372e25adec0219ee2334a68d2f5222e0cba9c0d613394e12d86", size = 243001, upload-time = "2025-06-13T13:01:35.285Z" }, - { url = "https://files.pythonhosted.org/packages/f5/e8/eed18aa5583b0423ab7f04e34659e51101135c41cd1dcb33ac1d7013a6d6/coverage-7.9.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34ed2186fe52fcc24d4561041979a0dec69adae7bce2ae8d1c49eace13e55c43", size = 244985, upload-time = "2025-06-13T13:01:36.712Z" }, - { url = "https://files.pythonhosted.org/packages/17/f8/ae9e5cce8885728c934eaa58ebfa8281d488ef2afa81c3dbc8ee9e6d80db/coverage-7.9.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:25308bd3d00d5eedd5ae7d4357161f4df743e3c0240fa773ee1b0f75e6c7c0f1", size = 245152, upload-time = "2025-06-13T13:01:39.303Z" }, - { url = "https://files.pythonhosted.org/packages/5a/c8/272c01ae792bb3af9b30fac14d71d63371db227980682836ec388e2c57c0/coverage-7.9.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:73e9439310f65d55a5a1e0564b48e34f5369bee943d72c88378f2d576f5a5751", size = 243123, upload-time = "2025-06-13T13:01:40.727Z" }, - { url = "https://files.pythonhosted.org/packages/8c/d0/2819a1e3086143c094ab446e3bdf07138527a7b88cb235c488e78150ba7a/coverage-7.9.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:37ab6be0859141b53aa89412a82454b482c81cf750de4f29223d52268a86de67", size = 244506, upload-time = "2025-06-13T13:01:42.184Z" }, - { url = "https://files.pythonhosted.org/packages/8b/4e/9f6117b89152df7b6112f65c7a4ed1f2f5ec8e60c4be8f351d91e7acc848/coverage-7.9.1-cp313-cp313-win32.whl", hash = "sha256:64bdd969456e2d02a8b08aa047a92d269c7ac1f47e0c977675d550c9a0863643", size = 214766, upload-time = "2025-06-13T13:01:44.482Z" }, - { url = "https://files.pythonhosted.org/packages/27/0f/4b59f7c93b52c2c4ce7387c5a4e135e49891bb3b7408dcc98fe44033bbe0/coverage-7.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:be9e3f68ca9edb897c2184ad0eee815c635565dbe7a0e7e814dc1f7cbab92c0a", size = 215568, upload-time = "2025-06-13T13:01:45.772Z" }, - { url = "https://files.pythonhosted.org/packages/09/1e/9679826336f8c67b9c39a359352882b24a8a7aee48d4c9cad08d38d7510f/coverage-7.9.1-cp313-cp313-win_arm64.whl", hash = "sha256:1c503289ffef1d5105d91bbb4d62cbe4b14bec4d13ca225f9c73cde9bb46207d", size = 213939, upload-time = "2025-06-13T13:01:47.087Z" }, - { url = "https://files.pythonhosted.org/packages/bb/5b/5c6b4e7a407359a2e3b27bf9c8a7b658127975def62077d441b93a30dbe8/coverage-7.9.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0b3496922cb5f4215bf5caaef4cf12364a26b0be82e9ed6d050f3352cf2d7ef0", size = 213079, upload-time = "2025-06-13T13:01:48.554Z" }, - { url = "https://files.pythonhosted.org/packages/a2/22/1e2e07279fd2fd97ae26c01cc2186e2258850e9ec125ae87184225662e89/coverage-7.9.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9565c3ab1c93310569ec0d86b017f128f027cab0b622b7af288696d7ed43a16d", size = 213299, upload-time = "2025-06-13T13:01:49.997Z" }, - { url = "https://files.pythonhosted.org/packages/14/c0/4c5125a4b69d66b8c85986d3321520f628756cf524af810baab0790c7647/coverage-7.9.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2241ad5dbf79ae1d9c08fe52b36d03ca122fb9ac6bca0f34439e99f8327ac89f", size = 256535, upload-time = "2025-06-13T13:01:51.314Z" }, - { url = "https://files.pythonhosted.org/packages/81/8b/e36a04889dda9960be4263e95e777e7b46f1bb4fc32202612c130a20c4da/coverage-7.9.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bb5838701ca68b10ebc0937dbd0eb81974bac54447c55cd58dea5bca8451029", size = 252756, upload-time = "2025-06-13T13:01:54.403Z" }, - { url = "https://files.pythonhosted.org/packages/98/82/be04eff8083a09a4622ecd0e1f31a2c563dbea3ed848069e7b0445043a70/coverage-7.9.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b30a25f814591a8c0c5372c11ac8967f669b97444c47fd794926e175c4047ece", size = 254912, upload-time = "2025-06-13T13:01:56.769Z" }, - { url = "https://files.pythonhosted.org/packages/0f/25/c26610a2c7f018508a5ab958e5b3202d900422cf7cdca7670b6b8ca4e8df/coverage-7.9.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2d04b16a6062516df97969f1ae7efd0de9c31eb6ebdceaa0d213b21c0ca1a683", size = 256144, upload-time = "2025-06-13T13:01:58.19Z" }, - { url = "https://files.pythonhosted.org/packages/c5/8b/fb9425c4684066c79e863f1e6e7ecebb49e3a64d9f7f7860ef1688c56f4a/coverage-7.9.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7931b9e249edefb07cd6ae10c702788546341d5fe44db5b6108a25da4dca513f", size = 254257, upload-time = "2025-06-13T13:01:59.645Z" }, - { url = "https://files.pythonhosted.org/packages/93/df/27b882f54157fc1131e0e215b0da3b8d608d9b8ef79a045280118a8f98fe/coverage-7.9.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:52e92b01041151bf607ee858e5a56c62d4b70f4dac85b8c8cb7fb8a351ab2c10", size = 255094, upload-time = "2025-06-13T13:02:01.37Z" }, - { url = "https://files.pythonhosted.org/packages/41/5f/cad1c3dbed8b3ee9e16fa832afe365b4e3eeab1fb6edb65ebbf745eabc92/coverage-7.9.1-cp313-cp313t-win32.whl", hash = "sha256:684e2110ed84fd1ca5f40e89aa44adf1729dc85444004111aa01866507adf363", size = 215437, upload-time = "2025-06-13T13:02:02.905Z" }, - { url = "https://files.pythonhosted.org/packages/99/4d/fad293bf081c0e43331ca745ff63673badc20afea2104b431cdd8c278b4c/coverage-7.9.1-cp313-cp313t-win_amd64.whl", hash = "sha256:437c576979e4db840539674e68c84b3cda82bc824dd138d56bead1435f1cb5d7", size = 216605, upload-time = "2025-06-13T13:02:05.638Z" }, - { url = "https://files.pythonhosted.org/packages/1f/56/4ee027d5965fc7fc126d7ec1187529cc30cc7d740846e1ecb5e92d31b224/coverage-7.9.1-cp313-cp313t-win_arm64.whl", hash = "sha256:18a0912944d70aaf5f399e350445738a1a20b50fbea788f640751c2ed9208b6c", size = 214392, upload-time = "2025-06-13T13:02:07.642Z" }, - { url = "https://files.pythonhosted.org/packages/a5/d6/c41dd9b02bf16ec001aaf1cbef665537606899a3db1094e78f5ae17540ca/coverage-7.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f424507f57878e424d9a95dc4ead3fbdd72fd201e404e861e465f28ea469951", size = 212029, upload-time = "2025-06-13T13:02:09.058Z" }, - { url = "https://files.pythonhosted.org/packages/f8/c0/40420d81d731f84c3916dcdf0506b3e6c6570817bff2576b83f780914ae6/coverage-7.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:535fde4001b2783ac80865d90e7cc7798b6b126f4cd8a8c54acfe76804e54e58", size = 212407, upload-time = "2025-06-13T13:02:11.151Z" }, - { url = "https://files.pythonhosted.org/packages/9b/87/f0db7d62d0e09f14d6d2f6ae8c7274a2f09edf74895a34b412a0601e375a/coverage-7.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02532fd3290bb8fa6bec876520842428e2a6ed6c27014eca81b031c2d30e3f71", size = 241160, upload-time = "2025-06-13T13:02:12.864Z" }, - { url = "https://files.pythonhosted.org/packages/a9/b7/3337c064f058a5d7696c4867159651a5b5fb01a5202bcf37362f0c51400e/coverage-7.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56f5eb308b17bca3bbff810f55ee26d51926d9f89ba92707ee41d3c061257e55", size = 239027, upload-time = "2025-06-13T13:02:14.294Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a9/5898a283f66d1bd413c32c2e0e05408196fd4f37e206e2b06c6e0c626e0e/coverage-7.9.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfa447506c1a52271f1b0de3f42ea0fa14676052549095e378d5bff1c505ff7b", size = 240145, upload-time = "2025-06-13T13:02:15.745Z" }, - { url = "https://files.pythonhosted.org/packages/e0/33/d96e3350078a3c423c549cb5b2ba970de24c5257954d3e4066e2b2152d30/coverage-7.9.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9ca8e220006966b4a7b68e8984a6aee645a0384b0769e829ba60281fe61ec4f7", size = 239871, upload-time = "2025-06-13T13:02:17.344Z" }, - { url = "https://files.pythonhosted.org/packages/1d/6e/6fb946072455f71a820cac144d49d11747a0f1a21038060a68d2d0200499/coverage-7.9.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:49f1d0788ba5b7ba65933f3a18864117c6506619f5ca80326b478f72acf3f385", size = 238122, upload-time = "2025-06-13T13:02:18.849Z" }, - { url = "https://files.pythonhosted.org/packages/e4/5c/bc43f25c8586840ce25a796a8111acf6a2b5f0909ba89a10d41ccff3920d/coverage-7.9.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:68cd53aec6f45b8e4724c0950ce86eacb775c6be01ce6e3669fe4f3a21e768ed", size = 239058, upload-time = "2025-06-13T13:02:21.423Z" }, - { url = "https://files.pythonhosted.org/packages/11/d8/ce2007418dd7fd00ff8c8b898bb150bb4bac2d6a86df05d7b88a07ff595f/coverage-7.9.1-cp39-cp39-win32.whl", hash = "sha256:95335095b6c7b1cc14c3f3f17d5452ce677e8490d101698562b2ffcacc304c8d", size = 214532, upload-time = "2025-06-13T13:02:22.857Z" }, - { url = "https://files.pythonhosted.org/packages/20/21/334e76fa246e92e6d69cab217f7c8a70ae0cc8f01438bd0544103f29528e/coverage-7.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:e1b5191d1648acc439b24721caab2fd0c86679d8549ed2c84d5a7ec1bedcc244", size = 215439, upload-time = "2025-06-13T13:02:24.268Z" }, - { url = "https://files.pythonhosted.org/packages/3e/e5/c723545c3fd3204ebde3b4cc4b927dce709d3b6dc577754bb57f63ca4a4a/coverage-7.9.1-pp39.pp310.pp311-none-any.whl", hash = "sha256:db0f04118d1db74db6c9e1cb1898532c7dcc220f1d2718f058601f7c3f499514", size = 204009, upload-time = "2025-06-13T13:02:25.787Z" }, - { url = "https://files.pythonhosted.org/packages/08/b8/7ddd1e8ba9701dea08ce22029917140e6f66a859427406579fd8d0ca7274/coverage-7.9.1-py3-none-any.whl", hash = "sha256:66b974b145aa189516b6bf2d8423e888b742517d37872f6ee4c5be0073bd9a3c", size = 204000, upload-time = "2025-06-13T13:02:27.173Z" }, -] - -[package.optional-dependencies] -toml = [ - { name = "tomli", marker = "python_full_version <= '3.11'" }, -] - -[[package]] -name = "distlib" -version = "0.3.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923, upload-time = "2024-10-09T18:35:47.551Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973, upload-time = "2024-10-09T18:35:44.272Z" }, -] - -[[package]] -name = "exceptiongroup" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, -] - -[[package]] -name = "filelock" -version = "3.18.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload-time = "2025-03-14T07:11:40.47Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" }, -] - -[[package]] -name = "ghp-import" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "python-dateutil" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943, upload-time = "2022-05-02T15:47:16.11Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" }, -] - -[[package]] -name = "griffe" -version = "1.7.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a9/3e/5aa9a61f7c3c47b0b52a1d930302992229d191bf4bc76447b324b731510a/griffe-1.7.3.tar.gz", hash = "sha256:52ee893c6a3a968b639ace8015bec9d36594961e156e23315c8e8e51401fa50b", size = 395137, upload-time = "2025-04-23T11:29:09.147Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/58/c6/5c20af38c2a57c15d87f7f38bee77d63c1d2a3689f74fefaf35915dd12b2/griffe-1.7.3-py3-none-any.whl", hash = "sha256:c6b3ee30c2f0f17f30bcdef5068d6ab7a2a4f1b8bf1a3e74b56fffd21e1c5f75", size = 129303, upload-time = "2025-04-23T11:29:07.145Z" }, -] - -[[package]] -name = "identify" -version = "2.6.12" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/88/d193a27416618628a5eea64e3223acd800b40749a96ffb322a9b55a49ed1/identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6", size = 99254, upload-time = "2025-05-23T20:37:53.3Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/cd/18f8da995b658420625f7ef13f037be53ae04ec5ad33f9b718240dcfd48c/identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2", size = 99145, upload-time = "2025-05-23T20:37:51.495Z" }, -] - -[[package]] -name = "idna" -version = "3.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, -] - -[[package]] -name = "importlib-metadata" -version = "8.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, -] - -[[package]] -name = "jinja2" -version = "3.1.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, -] - -[[package]] -name = "markdown" -version = "3.8.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d7/c2/4ab49206c17f75cb08d6311171f2d65798988db4360c4d1485bd0eedd67c/markdown-3.8.2.tar.gz", hash = "sha256:247b9a70dd12e27f67431ce62523e675b866d254f900c4fe75ce3dda62237c45", size = 362071, upload-time = "2025-06-19T17:12:44.483Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/2b/34cc11786bc00d0f04d0f5fdc3a2b1ae0b6239eef72d3d345805f9ad92a1/markdown-3.8.2-py3-none-any.whl", hash = "sha256:5c83764dbd4e00bdd94d85a19b8d55ccca20fe35b2e678a1422b380324dd5f24", size = 106827, upload-time = "2025-06-19T17:12:42.994Z" }, -] - -[[package]] -name = "markupsafe" -version = "3.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357, upload-time = "2024-10-18T15:20:51.44Z" }, - { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393, upload-time = "2024-10-18T15:20:52.426Z" }, - { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732, upload-time = "2024-10-18T15:20:53.578Z" }, - { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866, upload-time = "2024-10-18T15:20:55.06Z" }, - { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964, upload-time = "2024-10-18T15:20:55.906Z" }, - { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977, upload-time = "2024-10-18T15:20:57.189Z" }, - { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366, upload-time = "2024-10-18T15:20:58.235Z" }, - { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091, upload-time = "2024-10-18T15:20:59.235Z" }, - { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065, upload-time = "2024-10-18T15:21:00.307Z" }, - { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514, upload-time = "2024-10-18T15:21:01.122Z" }, - { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353, upload-time = "2024-10-18T15:21:02.187Z" }, - { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392, upload-time = "2024-10-18T15:21:02.941Z" }, - { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984, upload-time = "2024-10-18T15:21:03.953Z" }, - { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120, upload-time = "2024-10-18T15:21:06.495Z" }, - { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032, upload-time = "2024-10-18T15:21:07.295Z" }, - { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057, upload-time = "2024-10-18T15:21:08.073Z" }, - { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359, upload-time = "2024-10-18T15:21:09.318Z" }, - { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306, upload-time = "2024-10-18T15:21:10.185Z" }, - { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094, upload-time = "2024-10-18T15:21:11.005Z" }, - { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521, upload-time = "2024-10-18T15:21:12.911Z" }, - { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" }, - { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" }, - { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" }, - { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" }, - { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" }, - { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" }, - { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" }, - { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" }, - { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" }, - { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, - { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, - { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, - { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, - { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, - { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, - { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, - { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, - { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, - { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, - { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, - { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, - { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, - { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, - { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, - { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, - { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, - { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, - { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, - { url = "https://files.pythonhosted.org/packages/a7/ea/9b1530c3fdeeca613faeb0fb5cbcf2389d816072fab72a71b45749ef6062/MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", size = 14344, upload-time = "2024-10-18T15:21:43.721Z" }, - { url = "https://files.pythonhosted.org/packages/4b/c2/fbdbfe48848e7112ab05e627e718e854d20192b674952d9042ebd8c9e5de/MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", size = 12389, upload-time = "2024-10-18T15:21:44.666Z" }, - { url = "https://files.pythonhosted.org/packages/f0/25/7a7c6e4dbd4f867d95d94ca15449e91e52856f6ed1905d58ef1de5e211d0/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", size = 21607, upload-time = "2024-10-18T15:21:45.452Z" }, - { url = "https://files.pythonhosted.org/packages/53/8f/f339c98a178f3c1e545622206b40986a4c3307fe39f70ccd3d9df9a9e425/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", size = 20728, upload-time = "2024-10-18T15:21:46.295Z" }, - { url = "https://files.pythonhosted.org/packages/1a/03/8496a1a78308456dbd50b23a385c69b41f2e9661c67ea1329849a598a8f9/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", size = 20826, upload-time = "2024-10-18T15:21:47.134Z" }, - { url = "https://files.pythonhosted.org/packages/e6/cf/0a490a4bd363048c3022f2f475c8c05582179bb179defcee4766fb3dcc18/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", size = 21843, upload-time = "2024-10-18T15:21:48.334Z" }, - { url = "https://files.pythonhosted.org/packages/19/a3/34187a78613920dfd3cdf68ef6ce5e99c4f3417f035694074beb8848cd77/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", size = 21219, upload-time = "2024-10-18T15:21:49.587Z" }, - { url = "https://files.pythonhosted.org/packages/17/d8/5811082f85bb88410ad7e452263af048d685669bbbfb7b595e8689152498/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", size = 20946, upload-time = "2024-10-18T15:21:50.441Z" }, - { url = "https://files.pythonhosted.org/packages/7c/31/bd635fb5989440d9365c5e3c47556cfea121c7803f5034ac843e8f37c2f2/MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", size = 15063, upload-time = "2024-10-18T15:21:51.385Z" }, - { url = "https://files.pythonhosted.org/packages/b3/73/085399401383ce949f727afec55ec3abd76648d04b9f22e1c0e99cb4bec3/MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", size = 15506, upload-time = "2024-10-18T15:21:52.974Z" }, -] - -[[package]] -name = "mergedeep" -version = "1.3.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" }, -] - -[[package]] -name = "mkdocs" -version = "1.6.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "ghp-import" }, - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, - { name = "jinja2" }, - { name = "markdown" }, - { name = "markupsafe" }, - { name = "mergedeep" }, - { name = "mkdocs-get-deps" }, - { name = "packaging" }, - { name = "pathspec" }, - { name = "pyyaml" }, - { name = "pyyaml-env-tag" }, - { name = "watchdog" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload-time = "2024-08-30T12:24:06.899Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" }, -] - -[[package]] -name = "mkdocs-autorefs" -version = "1.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown" }, - { name = "markupsafe" }, - { name = "mkdocs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/47/0c/c9826f35b99c67fa3a7cddfa094c1a6c43fafde558c309c6e4403e5b37dc/mkdocs_autorefs-1.4.2.tar.gz", hash = "sha256:e2ebe1abd2b67d597ed19378c0fff84d73d1dbce411fce7a7cc6f161888b6749", size = 54961, upload-time = "2025-05-20T13:09:09.886Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/87/dc/fc063b78f4b769d1956319351704e23ebeba1e9e1d6a41b4b602325fd7e4/mkdocs_autorefs-1.4.2-py3-none-any.whl", hash = "sha256:83d6d777b66ec3c372a1aad4ae0cf77c243ba5bcda5bf0c6b8a2c5e7a3d89f13", size = 24969, upload-time = "2025-05-20T13:09:08.237Z" }, -] - -[[package]] -name = "mkdocs-get-deps" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, - { name = "mergedeep" }, - { name = "platformdirs" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239, upload-time = "2023-11-20T17:51:09.981Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521, upload-time = "2023-11-20T17:51:08.587Z" }, -] - -[[package]] -name = "mkdocs-material" -version = "9.6.14" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "babel" }, - { name = "backrefs" }, - { name = "colorama" }, - { name = "jinja2" }, - { name = "markdown" }, - { name = "mkdocs" }, - { name = "mkdocs-material-extensions" }, - { name = "paginate" }, - { name = "pygments" }, - { name = "pymdown-extensions" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fa/0101de32af88f87cf5cc23ad5f2e2030d00995f74e616306513431b8ab4b/mkdocs_material-9.6.14.tar.gz", hash = "sha256:39d795e90dce6b531387c255bd07e866e027828b7346d3eba5ac3de265053754", size = 3951707, upload-time = "2025-05-13T13:27:57.173Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/a1/7fdb959ad592e013c01558822fd3c22931a95a0f08cf0a7c36da13a5b2b5/mkdocs_material-9.6.14-py3-none-any.whl", hash = "sha256:3b9cee6d3688551bf7a8e8f41afda97a3c39a12f0325436d76c86706114b721b", size = 8703767, upload-time = "2025-05-13T13:27:54.089Z" }, -] - -[[package]] -name = "mkdocs-material-extensions" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847, upload-time = "2023-11-22T19:09:45.208Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728, upload-time = "2023-11-22T19:09:43.465Z" }, -] - -[[package]] -name = "mkdocstrings" -version = "0.29.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, - { name = "jinja2" }, - { name = "markdown" }, - { name = "markupsafe" }, - { name = "mkdocs" }, - { name = "mkdocs-autorefs" }, - { name = "pymdown-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/41/e8/d22922664a627a0d3d7ff4a6ca95800f5dde54f411982591b4621a76225d/mkdocstrings-0.29.1.tar.gz", hash = "sha256:8722f8f8c5cd75da56671e0a0c1bbed1df9946c0cef74794d6141b34011abd42", size = 1212686, upload-time = "2025-03-31T08:33:11.997Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/14/22533a578bf8b187e05d67e2c1721ce10e3f526610eebaf7a149d557ea7a/mkdocstrings-0.29.1-py3-none-any.whl", hash = "sha256:37a9736134934eea89cbd055a513d40a020d87dfcae9e3052c2a6b8cd4af09b6", size = 1631075, upload-time = "2025-03-31T08:33:09.661Z" }, -] - -[package.optional-dependencies] -python = [ - { name = "mkdocstrings-python" }, -] - -[[package]] -name = "mkdocstrings-python" -version = "1.16.12" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "griffe" }, - { name = "mkdocs-autorefs" }, - { name = "mkdocstrings" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bf/ed/b886f8c714fd7cccc39b79646b627dbea84cd95c46be43459ef46852caf0/mkdocstrings_python-1.16.12.tar.gz", hash = "sha256:9b9eaa066e0024342d433e332a41095c4e429937024945fea511afe58f63175d", size = 206065, upload-time = "2025-06-03T12:52:49.276Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/dd/a24ee3de56954bfafb6ede7cd63c2413bb842cc48eb45e41c43a05a33074/mkdocstrings_python-1.16.12-py3-none-any.whl", hash = "sha256:22ded3a63b3d823d57457a70ff9860d5a4de9e8b1e482876fc9baabaf6f5f374", size = 124287, upload-time = "2025-06-03T12:52:47.819Z" }, -] - -[[package]] -name = "nodeenv" -version = "1.9.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, -] - -[[package]] -name = "packaging" -version = "25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, -] - -[[package]] -name = "paginate" -version = "0.5.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252, upload-time = "2024-08-25T14:17:24.139Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746, upload-time = "2024-08-25T14:17:22.55Z" }, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.3.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" }, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, -] - -[[package]] -name = "pre-commit" -version = "4.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cfgv" }, - { name = "identify" }, - { name = "nodeenv" }, - { name = "pyyaml" }, - { name = "virtualenv" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/08/39/679ca9b26c7bb2999ff122d50faa301e49af82ca9c066ec061cfbc0c6784/pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146", size = 193424, upload-time = "2025-03-18T21:35:20.987Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/74/a88bf1b1efeae488a0c0b7bdf71429c313722d1fc0f377537fbe554e6180/pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd", size = 220707, upload-time = "2025-03-18T21:35:19.343Z" }, -] - -[[package]] -name = "pygments" -version = "2.19.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" }, -] - -[[package]] -name = "pymdown-extensions" -version = "10.15" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/08/92/a7296491dbf5585b3a987f3f3fc87af0e632121ff3e490c14b5f2d2b4eb5/pymdown_extensions-10.15.tar.gz", hash = "sha256:0e5994e32155f4b03504f939e501b981d306daf7ec2aa1cd2eb6bd300784f8f7", size = 852320, upload-time = "2025-04-27T23:48:29.183Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/d1/c54e608505776ce4e7966d03358ae635cfd51dff1da6ee421c090dbc797b/pymdown_extensions-10.15-py3-none-any.whl", hash = "sha256:46e99bb272612b0de3b7e7caf6da8dd5f4ca5212c0b273feb9304e236c484e5f", size = 265845, upload-time = "2025-04-27T23:48:27.359Z" }, -] - -[[package]] -name = "pytest" -version = "8.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, -] - -[[package]] -name = "pytest-cov" -version = "6.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "coverage", extra = ["toml"] }, - { name = "pluggy" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432, upload-time = "2025-06-12T10:47:47.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" }, -] - -[[package]] -name = "python-collab-template" -version = "0.1.0" -source = { editable = "." } - -[package.optional-dependencies] -dev = [ - { name = "pre-commit" }, - { name = "pytest" }, - { name = "pytest-cov" }, - { name = "ruff" }, - { name = "tomli" }, - { name = "tomli-w" }, - { name = "ty" }, -] - -[package.dev-dependencies] -dev = [ - { name = "mkdocs-material" }, - { name = "mkdocstrings", extra = ["python"] }, -] - -[package.metadata] -requires-dist = [ - { name = "pre-commit", marker = "extra == 'dev'", specifier = ">=3.6.0" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.1.1" }, - { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=5.0.0" }, - { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.3.0" }, - { name = "tomli", marker = "extra == 'dev'", specifier = ">=2.0.1" }, - { name = "tomli-w", marker = "extra == 'dev'", specifier = ">=1.0.0" }, - { name = "ty", marker = "extra == 'dev'", specifier = ">=0.0.2" }, -] -provides-extras = ["dev"] - -[package.metadata.requires-dev] -dev = [ - { name = "mkdocs-material", specifier = ">=9.6.14" }, - { name = "mkdocstrings", extras = ["python"], specifier = ">=0.26.1" }, -] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, - { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" }, - { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" }, - { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" }, - { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" }, - { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" }, - { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" }, - { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" }, - { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" }, - { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, - { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, - { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, - { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, - { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, - { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, - { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, - { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" }, - { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, - { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, - { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, - { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, - { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, - { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, - { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, - { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, - { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, - { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, - { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, - { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, - { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, - { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, - { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, - { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777, upload-time = "2024-08-06T20:33:25.896Z" }, - { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318, upload-time = "2024-08-06T20:33:27.212Z" }, - { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891, upload-time = "2024-08-06T20:33:28.974Z" }, - { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614, upload-time = "2024-08-06T20:33:34.157Z" }, - { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360, upload-time = "2024-08-06T20:33:35.84Z" }, - { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006, upload-time = "2024-08-06T20:33:37.501Z" }, - { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577, upload-time = "2024-08-06T20:33:39.389Z" }, - { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593, upload-time = "2024-08-06T20:33:46.63Z" }, - { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312, upload-time = "2024-08-06T20:33:49.073Z" }, -] - -[[package]] -name = "pyyaml-env-tag" -version = "1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737, upload-time = "2025-05-13T15:24:01.64Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" }, -] - -[[package]] -name = "requests" -version = "2.32.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, -] - -[[package]] -name = "ruff" -version = "0.12.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/90/5255432602c0b196a0da6720f6f76b93eb50baef46d3c9b0025e2f9acbf3/ruff-0.12.0.tar.gz", hash = "sha256:4d047db3662418d4a848a3fdbfaf17488b34b62f527ed6f10cb8afd78135bc5c", size = 4376101, upload-time = "2025-06-17T15:19:26.217Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/fd/b46bb20e14b11ff49dbc74c61de352e0dc07fb650189513631f6fb5fc69f/ruff-0.12.0-py3-none-linux_armv6l.whl", hash = "sha256:5652a9ecdb308a1754d96a68827755f28d5dfb416b06f60fd9e13f26191a8848", size = 10311554, upload-time = "2025-06-17T15:18:45.792Z" }, - { url = "https://files.pythonhosted.org/packages/e7/d3/021dde5a988fa3e25d2468d1dadeea0ae89dc4bc67d0140c6e68818a12a1/ruff-0.12.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:05ed0c914fabc602fc1f3b42c53aa219e5736cb030cdd85640c32dbc73da74a6", size = 11118435, upload-time = "2025-06-17T15:18:49.064Z" }, - { url = "https://files.pythonhosted.org/packages/07/a2/01a5acf495265c667686ec418f19fd5c32bcc326d4c79ac28824aecd6a32/ruff-0.12.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:07a7aa9b69ac3fcfda3c507916d5d1bca10821fe3797d46bad10f2c6de1edda0", size = 10466010, upload-time = "2025-06-17T15:18:51.341Z" }, - { url = "https://files.pythonhosted.org/packages/4c/57/7caf31dd947d72e7aa06c60ecb19c135cad871a0a8a251723088132ce801/ruff-0.12.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7731c3eec50af71597243bace7ec6104616ca56dda2b99c89935fe926bdcd48", size = 10661366, upload-time = "2025-06-17T15:18:53.29Z" }, - { url = "https://files.pythonhosted.org/packages/e9/ba/aa393b972a782b4bc9ea121e0e358a18981980856190d7d2b6187f63e03a/ruff-0.12.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:952d0630eae628250ab1c70a7fffb641b03e6b4a2d3f3ec6c1d19b4ab6c6c807", size = 10173492, upload-time = "2025-06-17T15:18:55.262Z" }, - { url = "https://files.pythonhosted.org/packages/d7/50/9349ee777614bc3062fc6b038503a59b2034d09dd259daf8192f56c06720/ruff-0.12.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c021f04ea06966b02614d442e94071781c424ab8e02ec7af2f037b4c1e01cc82", size = 11761739, upload-time = "2025-06-17T15:18:58.906Z" }, - { url = "https://files.pythonhosted.org/packages/04/8f/ad459de67c70ec112e2ba7206841c8f4eb340a03ee6a5cabc159fe558b8e/ruff-0.12.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7d235618283718ee2fe14db07f954f9b2423700919dc688eacf3f8797a11315c", size = 12537098, upload-time = "2025-06-17T15:19:01.316Z" }, - { url = "https://files.pythonhosted.org/packages/ed/50/15ad9c80ebd3c4819f5bd8883e57329f538704ed57bac680d95cb6627527/ruff-0.12.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c0758038f81beec8cc52ca22de9685b8ae7f7cc18c013ec2050012862cc9165", size = 12154122, upload-time = "2025-06-17T15:19:03.727Z" }, - { url = "https://files.pythonhosted.org/packages/76/e6/79b91e41bc8cc3e78ee95c87093c6cacfa275c786e53c9b11b9358026b3d/ruff-0.12.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:139b3d28027987b78fc8d6cfb61165447bdf3740e650b7c480744873688808c2", size = 11363374, upload-time = "2025-06-17T15:19:05.875Z" }, - { url = "https://files.pythonhosted.org/packages/db/c3/82b292ff8a561850934549aa9dc39e2c4e783ab3c21debe55a495ddf7827/ruff-0.12.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68853e8517b17bba004152aebd9dd77d5213e503a5f2789395b25f26acac0da4", size = 11587647, upload-time = "2025-06-17T15:19:08.246Z" }, - { url = "https://files.pythonhosted.org/packages/2b/42/d5760d742669f285909de1bbf50289baccb647b53e99b8a3b4f7ce1b2001/ruff-0.12.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3a9512af224b9ac4757f7010843771da6b2b0935a9e5e76bb407caa901a1a514", size = 10527284, upload-time = "2025-06-17T15:19:10.37Z" }, - { url = "https://files.pythonhosted.org/packages/19/f6/fcee9935f25a8a8bba4adbae62495c39ef281256693962c2159e8b284c5f/ruff-0.12.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b08df3d96db798e5beb488d4df03011874aff919a97dcc2dd8539bb2be5d6a88", size = 10158609, upload-time = "2025-06-17T15:19:12.286Z" }, - { url = "https://files.pythonhosted.org/packages/37/fb/057febf0eea07b9384787bfe197e8b3384aa05faa0d6bd844b94ceb29945/ruff-0.12.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6a315992297a7435a66259073681bb0d8647a826b7a6de45c6934b2ca3a9ed51", size = 11141462, upload-time = "2025-06-17T15:19:15.195Z" }, - { url = "https://files.pythonhosted.org/packages/10/7c/1be8571011585914b9d23c95b15d07eec2d2303e94a03df58294bc9274d4/ruff-0.12.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1e55e44e770e061f55a7dbc6e9aed47feea07731d809a3710feda2262d2d4d8a", size = 11641616, upload-time = "2025-06-17T15:19:17.6Z" }, - { url = "https://files.pythonhosted.org/packages/6a/ef/b960ab4818f90ff59e571d03c3f992828d4683561095e80f9ef31f3d58b7/ruff-0.12.0-py3-none-win32.whl", hash = "sha256:7162a4c816f8d1555eb195c46ae0bd819834d2a3f18f98cc63819a7b46f474fb", size = 10525289, upload-time = "2025-06-17T15:19:19.688Z" }, - { url = "https://files.pythonhosted.org/packages/34/93/8b16034d493ef958a500f17cda3496c63a537ce9d5a6479feec9558f1695/ruff-0.12.0-py3-none-win_amd64.whl", hash = "sha256:d00b7a157b8fb6d3827b49d3324da34a1e3f93492c1f97b08e222ad7e9b291e0", size = 11598311, upload-time = "2025-06-17T15:19:21.785Z" }, - { url = "https://files.pythonhosted.org/packages/d0/33/4d3e79e4a84533d6cd526bfb42c020a23256ae5e4265d858bd1287831f7d/ruff-0.12.0-py3-none-win_arm64.whl", hash = "sha256:8cd24580405ad8c1cc64d61725bca091d6b6da7eb3d36f72cc605467069d7e8b", size = 10724946, upload-time = "2025-06-17T15:19:23.952Z" }, -] - -[[package]] -name = "six" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, -] - -[[package]] -name = "tomli" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, - { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, - { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, - { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, - { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, - { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, - { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, - { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, - { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, - { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, - { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, - { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, - { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, - { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, - { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, - { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, - { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, - { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, - { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, - { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, - { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, - { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, - { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, - { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, - { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, - { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, - { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, - { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, -] - -[[package]] -name = "tomli-w" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" }, -] - -[[package]] -name = "ty" -version = "0.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/e5/15b6aceefcd64b53997fe2002b6fa055f0b1afd23ff6fc3f55f3da944530/ty-0.0.2.tar.gz", hash = "sha256:e02dc50b65dc58d6cb8e8b0d563833f81bf03ed8a7d0b15c6396d486489a7e1d", size = 4762024, upload-time = "2025-12-16T20:13:41.07Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/86/65d4826677d966cf226662767a4a597ebb4b02c432f413673c8d5d3d1ce8/ty-0.0.2-py3-none-linux_armv6l.whl", hash = "sha256:0954a0e0b6f7e06229dd1da3a9989ee9b881a26047139a88eb7c134c585ad22e", size = 9771409, upload-time = "2025-12-16T20:13:28.964Z" }, - { url = "https://files.pythonhosted.org/packages/d4/bc/6ab06b7c109cec608c24ea182cc8b4714e746a132f70149b759817092665/ty-0.0.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d6044b491d66933547033cecc87cb7eb599ba026a3ef347285add6b21107a648", size = 9580025, upload-time = "2025-12-16T20:13:34.507Z" }, - { url = "https://files.pythonhosted.org/packages/54/de/d826804e304b2430f17bb27ae15bcf02380e7f67f38b5033047e3d2523e6/ty-0.0.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fbca7f08e671a35229f6f400d73da92e2dc0a440fba53a74fe8233079a504358", size = 9098660, upload-time = "2025-12-16T20:13:01.278Z" }, - { url = "https://files.pythonhosted.org/packages/b7/8e/5cd87944ceee02bb0826f19ced54e30c6bb971e985a22768f6be6b1a042f/ty-0.0.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3abd61153dac0b93b284d305e6f96085013a25c3a7ab44e988d24f0a5fcce729", size = 9567693, upload-time = "2025-12-16T20:13:12.559Z" }, - { url = "https://files.pythonhosted.org/packages/c6/b1/062aab2c62c5ae01c05d27b97ba022d9ff66f14a3cb9030c5ad1dca797ec/ty-0.0.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:21a9f28caafb5742e7d594104e2fe2ebd64590da31aed4745ae8bc5be67a7b85", size = 9556471, upload-time = "2025-12-16T20:13:07.771Z" }, - { url = "https://files.pythonhosted.org/packages/0e/07/856f6647a9dd6e36560d182d35d3b5fb21eae98a8bfb516cd879d0e509f3/ty-0.0.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3ec63fd23ab48e0f838fb54a47ec362a972ee80979169a7edfa6f5c5034849d", size = 9971914, upload-time = "2025-12-16T20:13:18.852Z" }, - { url = "https://files.pythonhosted.org/packages/2e/82/c2e3957dbf33a23f793a9239cfd8bd04b6defd999bd0f6e74d6a5afb9f42/ty-0.0.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e5e2e0293a259c9a53f668c9c13153cc2f1403cb0fe2b886ca054be4ac76517c", size = 10840905, upload-time = "2025-12-16T20:13:37.098Z" }, - { url = "https://files.pythonhosted.org/packages/3b/17/49bd74e3d577e6c88b8074581b7382f532a9d40552cc7c48ceaa83f1d950/ty-0.0.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fd2511ac02a83d0dc45d4570c7e21ec0c919be7a7263bad9914800d0cde47817", size = 10570251, upload-time = "2025-12-16T20:13:10.319Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9b/26741834069722033a1a0963fcbb63ea45925c6697357e64e361753c6166/ty-0.0.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c482bfbfb8ad18b2e62427d02a0c934ac510c414188a3cf00e16b8acc35482f0", size = 10369078, upload-time = "2025-12-16T20:13:20.851Z" }, - { url = "https://files.pythonhosted.org/packages/94/fc/1d34ec891900d9337169ff9f8252fcaa633ae5c4d36b67effd849ed4f9ac/ty-0.0.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb514711eed3f56d7a130d4885f4b5d8e490fdcd2adac098e5cf175573a0dda3", size = 10121064, upload-time = "2025-12-16T20:13:23.095Z" }, - { url = "https://files.pythonhosted.org/packages/e5/02/e640325956172355ef8deb9b08d991f229230bf9d07f1dbda8c6665a3a43/ty-0.0.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b2c37fa26c39e9fbed7c73645ba721968ab44f28b2bfe2f79a4e15965a1c426f", size = 9553817, upload-time = "2025-12-16T20:13:27.057Z" }, - { url = "https://files.pythonhosted.org/packages/35/13/c93d579ece84895da9b0aae5d34d84100bbff63ad9f60c906a533a087175/ty-0.0.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:13b264833ac5f3b214693fca38e380e78ee7327e09beaa5ff2e47d75fcab9692", size = 9577512, upload-time = "2025-12-16T20:13:16.956Z" }, - { url = "https://files.pythonhosted.org/packages/85/53/93ab1570adc799cd9120ea187d5b4c00d821e86eca069943b179fe0d3e83/ty-0.0.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:08658d6dbbf8bdef80c0a77eda56a22ab6737002ba129301b7bbd36bcb7acd75", size = 9692726, upload-time = "2025-12-16T20:13:31.169Z" }, - { url = "https://files.pythonhosted.org/packages/9a/07/5fff5335858a14196776207d231c32e23e48a5c912a7d52c80e7a3fa6f8f/ty-0.0.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:4a21b5b012061cb13d47edfff6be70052694308dba633b4c819b70f840e6c158", size = 10213996, upload-time = "2025-12-16T20:13:14.606Z" }, - { url = "https://files.pythonhosted.org/packages/a0/d3/896b1439ab765c57a8d732f73c105ec41142c417a582600638385c2bee85/ty-0.0.2-py3-none-win32.whl", hash = "sha256:d773fdad5d2b30f26313204e6b191cdd2f41ab440a6c241fdb444f8c6593c288", size = 9204906, upload-time = "2025-12-16T20:13:25.099Z" }, - { url = "https://files.pythonhosted.org/packages/5d/0a/f30981e7d637f78e3d08e77d63b818752d23db1bc4b66f9e82e2cb3d34f8/ty-0.0.2-py3-none-win_amd64.whl", hash = "sha256:d1c9ac78a8aa60d0ce89acdccf56c3cc0fcb2de07f1ecf313754d83518e8e8c5", size = 10066640, upload-time = "2025-12-16T20:13:04.045Z" }, - { url = "https://files.pythonhosted.org/packages/5a/c4/97958503cf62bfb7908d2a77b03b91a20499a7ff405f5a098c4989589f34/ty-0.0.2-py3-none-win_arm64.whl", hash = "sha256:fbdef644ade0cd4420c4ec14b604b7894cefe77bfd8659686ac2f6aba9d1a306", size = 9572022, upload-time = "2025-12-16T20:13:39.189Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423, upload-time = "2025-06-02T14:52:11.399Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839, upload-time = "2025-06-02T14:52:10.026Z" }, -] - -[[package]] -name = "urllib3" -version = "2.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, -] - -[[package]] -name = "virtualenv" -version = "20.31.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "distlib" }, - { name = "filelock" }, - { name = "platformdirs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/56/2c/444f465fb2c65f40c3a104fd0c495184c4f2336d65baf398e3c75d72ea94/virtualenv-20.31.2.tar.gz", hash = "sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af", size = 6076316, upload-time = "2025-05-08T17:58:23.811Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/40/b1c265d4b2b62b58576588510fc4d1fe60a86319c8de99fd8e9fec617d2c/virtualenv-20.31.2-py3-none-any.whl", hash = "sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11", size = 6057982, upload-time = "2025-05-08T17:58:21.15Z" }, -] - -[[package]] -name = "watchdog" -version = "6.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/56/90994d789c61df619bfc5ce2ecdabd5eeff564e1eb47512bd01b5e019569/watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26", size = 96390, upload-time = "2024-11-01T14:06:24.793Z" }, - { url = "https://files.pythonhosted.org/packages/55/46/9a67ee697342ddf3c6daa97e3a587a56d6c4052f881ed926a849fcf7371c/watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112", size = 88389, upload-time = "2024-11-01T14:06:27.112Z" }, - { url = "https://files.pythonhosted.org/packages/44/65/91b0985747c52064d8701e1075eb96f8c40a79df889e59a399453adfb882/watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3", size = 89020, upload-time = "2024-11-01T14:06:29.876Z" }, - { url = "https://files.pythonhosted.org/packages/e0/24/d9be5cd6642a6aa68352ded4b4b10fb0d7889cb7f45814fb92cecd35f101/watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c", size = 96393, upload-time = "2024-11-01T14:06:31.756Z" }, - { url = "https://files.pythonhosted.org/packages/63/7a/6013b0d8dbc56adca7fdd4f0beed381c59f6752341b12fa0886fa7afc78b/watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2", size = 88392, upload-time = "2024-11-01T14:06:32.99Z" }, - { url = "https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c", size = 89019, upload-time = "2024-11-01T14:06:34.963Z" }, - { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" }, - { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" }, - { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" }, - { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, - { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, - { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, - { url = "https://files.pythonhosted.org/packages/05/52/7223011bb760fce8ddc53416beb65b83a3ea6d7d13738dde75eeb2c89679/watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8", size = 96390, upload-time = "2024-11-01T14:06:49.325Z" }, - { url = "https://files.pythonhosted.org/packages/9c/62/d2b21bc4e706d3a9d467561f487c2938cbd881c69f3808c43ac1ec242391/watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a", size = 88386, upload-time = "2024-11-01T14:06:50.536Z" }, - { url = "https://files.pythonhosted.org/packages/ea/22/1c90b20eda9f4132e4603a26296108728a8bfe9584b006bd05dd94548853/watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c", size = 89017, upload-time = "2024-11-01T14:06:51.717Z" }, - { url = "https://files.pythonhosted.org/packages/30/ad/d17b5d42e28a8b91f8ed01cb949da092827afb9995d4559fd448d0472763/watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881", size = 87902, upload-time = "2024-11-01T14:06:53.119Z" }, - { url = "https://files.pythonhosted.org/packages/5c/ca/c3649991d140ff6ab67bfc85ab42b165ead119c9e12211e08089d763ece5/watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11", size = 88380, upload-time = "2024-11-01T14:06:55.19Z" }, - { url = "https://files.pythonhosted.org/packages/5b/79/69f2b0e8d3f2afd462029031baafb1b75d11bb62703f0e1022b2e54d49ee/watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa", size = 87903, upload-time = "2024-11-01T14:06:57.052Z" }, - { url = "https://files.pythonhosted.org/packages/e2/2b/dc048dd71c2e5f0f7ebc04dd7912981ec45793a03c0dc462438e0591ba5d/watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e", size = 88381, upload-time = "2024-11-01T14:06:58.193Z" }, - { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, - { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, - { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, - { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, - { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, - { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, - { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, - { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, -] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -]