Agent Guidance

May 27, 2026 · View on GitHub

Looking for instructions tailored to AI coding assistants?

Use CLAUDE.md. It is the canonical, maintained guide for all agents (Claude, Cursor, ChatGPT, etc.) that interact with this repository.

PR Title Conventions for AI Assistants

This project uses Release Please for automated changelog generation. When creating PRs, use conventional commit format for PR titles:

Required Format

<type>[optional scope]: <description>

Supported Types

  • feat: New features (triggers MINOR version)
  • fix: Bug fixes (triggers PATCH version)
  • revert: Reverts of previous changes (triggers PATCH version)
  • docs: Documentation changes (triggers PATCH version)
  • chore: Maintenance, dependencies (triggers PATCH version)
  • refactor: Code refactoring (triggers PATCH version)
  • style: Formatting, linting (triggers PATCH version)
  • test: Test changes (triggers PATCH version)
  • perf: Performance improvements (triggers MINOR version)
  • ci: CI/CD changes (triggers PATCH version)
  • build: Build system changes (triggers PATCH version)

When reverting a change, use revert: (not the default Revert "..." subject) so Release Please records it under the Reverts section of the changelog. See CONTRIBUTING.md for the full revert workflow.

Breaking Changes

Add ! to indicate breaking changes (triggers MAJOR version):

  • feat!: remove deprecated API
  • fix!: change database schema

Examples

feat: add OAuth authentication support
fix: resolve Docker build failure
docs: update installation guide
chore: bump FastAPI dependency
feat!: remove deprecated authentication system

Why This Matters

  • PR titles become commit messages after squash
  • Every merged PR creates/updates a Release Please PR
  • Release happens when the Release Please PR is merged
  • Automatic changelog generation from PR titles
  • Professional release notes for users

Release Workflow

  1. Merge any PR → Release Please creates/updates a release PR
  2. Review the release PR (version bump + changelog)
  3. Merge the release PR when ready to release
  4. Release Please publishes the release and triggers package publishing

See CONTRIBUTING.md for detailed guidelines.

Documentation Requirements

Every new feature or significant change must include documentation updates as part of the same PR, not as follow-up work. Update whichever of the following apply:

  • Docs site (vibetuner-docs/docs/) — the relevant page(s) for the feature area
  • llms.txt (vibetuner-docs/docs/llms.txt) — concise feature entry in the Features section
  • llms-full.txt (vibetuner-docs/docs/llms-full.txt) — detailed documentation with code examples
  • vibetuner-template/AGENTS.md — if the feature affects scaffolded projects (and the corresponding .claude/rules/ file if applicable)

If you are unsure which docs to update, update all four. Omitting docs from a feature PR is a review blocker.

Testing Scaffold Changes from a Branch

When working on scaffold-related changes (template updates, CLI improvements), you can test the scaffolding CLI directly from a branch without publishing to PyPI:

# Test scaffold command from a specific branch
uvx git+https://github.com/alltuner/vibetuner@BRANCH_NAME#subdirectory=vibetuner-py scaffold new --help

# Create a test project using the branch
uvx git+https://github.com/alltuner/vibetuner@BRANCH_NAME#subdirectory=vibetuner-py scaffold new /tmp/test-project

The scaffold command also accepts a -b parameter to specify the branch dynamically:

# Specify branch with -b parameter (when available)
uvx git+https://github.com/alltuner/vibetuner#subdirectory=vibetuner-py scaffold new -b BRANCH_NAME /tmp/test-project

When to use this:

  • Testing scaffold changes before merging a PR
  • Verifying bug fixes in template or CLI
  • Allowing others to test your changes before review
  • CI/CD integration testing

Note: The .tmp/ directory at the root is gitignored. Use it for testing scaffold commands if you have difficulty accessing external directories like /tmp:

# Test scaffolding in the repo's .tmp directory
uvx git+https://github.com/alltuner/vibetuner@BRANCH_NAME#subdirectory=vibetuner-py scaffold new ./.tmp/test-project

See vibetuner-docs/docs/development.md for complete development workflows.

Repository Justfile Commands

For developers working on the vibetuner repository itself, the root justfile provides commands for managing the monorepo.

Run just to see all available commands.

Dependencies

just update-all              # Update all package dependencies (py, js, template, root)
just update-and-commit       # Update all dependencies and commit changes
just update-py               # Update Python dependencies in vibetuner-py
just update-js               # Update JavaScript dependencies in vibetuner-js
just update-template         # Update dependencies in vibetuner-template
just update-root             # Update root scaffolding dependencies
just sync-jinja              # Refresh vibetuner-jinja/templates from vibetuner-py

The sync-jinja recipe mirrors vibetuner-py/src/vibetuner/templates/frontend/ into vibetuner-jinja/templates/, the npm package that ships the framework's core jinja templates so tailwind can @source them at frontend build time. The same script runs as the package's prepare lifecycle hook, so workspace bun install and npm publish keep things in sync automatically — just sync-jinja is just the manual entry point.

Documentation

just docs-serve              # Serve documentation locally with live reload
just docs-build              # Build documentation site

Code Quality

just format                  # Format all code (Python, Jinja, TOML, YAML)
just format-py               # Format Python with ruff
just format-jinja            # Format Jinja templates with djlint
just format-toml             # Format TOML files with taplo
just format-yaml             # Format YAML files with dprint

just lint                    # Run all linting checks
just lint-gh                 # Lint GitHub Actions workflows with actionlint
just lint-py                 # Lint Python with ruff
just lint-jinja              # Lint Jinja with djlint
just lint-md                 # Lint markdown files
just lint-toml               # Lint TOML with taplo
just lint-yaml               # Lint YAML with dprint
just type-check              # Type check at the repo root (template + tooling)
just type-check-py           # Type check the vibetuner-py framework code with ty

Testing (vibetuner-py)

Dev dependencies (pytest, pytest-asyncio) are declared as an optional extra in vibetuner-py/pyproject.toml. You must install them before running tests:

cd vibetuner-py
uv sync --extra dev
uv run python -m pytest tests/          # run all tests
uv run python -m pytest tests/unit/     # run unit tests only

Template Management

just update-frontend-templates  # Sync frontend templates and commit changes

Beanie (MongoDB ODM)

Document.get() accepts plain strings

Beanie's Document.get() accepts Any for document_id, so wrapping IDs with PydanticObjectId() is unnecessary:

# Correct - pass the string directly
doc = await MyDocument.get(some_id)

# Unnecessary - PydanticObjectId wrapping adds no value here
doc = await MyDocument.get(PydanticObjectId(some_id))

PydanticObjectId is still useful as a FastAPI path parameter type annotation (for request validation) and in explicit query filters, but .get() handles the conversion internally.

Event hook methods must NOT start with an underscore

Beanie's init_actions() skips any method whose name starts with _ when registering @before_event / @after_event hooks. A hook named _encrypt_on_save will silently never fire. Always use public names:

# Correct - Beanie discovers and registers this hook
@before_event(Insert)
def encrypt_on_insert(self) -> None: ...

# Wrong - silently ignored by Beanie's action registry
@before_event(Insert)
def _encrypt_on_insert(self) -> None: ...

Ad-hoc collection access uses PyMongo, not Motor

The project uses pymongo (not motor). For direct collection access, use get_pymongo_collection():

# Correct
collection = MyDocument.get_pymongo_collection()

# Wrong - motor was removed, this no longer exists
collection = MyDocument.get_motor_collection()  # AttributeError

Logging

All code in vibetuner-py/src/vibetuner/ must use the project's Loguru-based logger:

from vibetuner.logging import logger

Do not use import logging or logging.getLogger(...) directly. The only exception is vibetuner/logging.py itself, which defines the logging facility and necessarily imports the stdlib logging module.

The InterceptHandler in vibetuner/logging.py routes stdlib logging through Loguru, so third-party libraries still work. But framework code should always use the Loguru logger directly for consistent formatting, request-ID injection, and log-level control.

Markdown Line Length

This project enforces a 120 character line limit for markdown files using rumdl.

When editing markdown files:

  • Keep lines under 120 characters
  • Break long lines at natural points (commas, list items, sentence boundaries)
  • Indent continuation lines consistently with their parent items
  • Pre-commit hooks will catch violations before commit

Example of properly wrapped markdown:

1. Stage all relevant changes:
   - **IMPORTANT**: NEVER stage release-related files: `.release-please-manifest.json`,
     `CHANGELOG.md`, version changes in `pyproject.toml` or `package.json`
   - Use `git add` with explicit file paths for each file you want to commit