# AGENTS.md - Agentic Coding Guidelines for Diffdown This file provides guidelines for AI agents operating in this repository. ## Project Overview Diffdown is a collaborative Markdown editor with ATProto (Bluesky) authentication. It uses Go for the backend and vanilla JavaScript/CodeMirror for the frontend. --- ## Build, Lint, and Test Commands ### Makefile Targets | Command | Description | |---------|-------------| | `make build` | Build the Go application to `bin/server` | | `make run` | Run the server directly with `go run` | | `make test` | Run all tests with verbose output | | `make test-coverage` | Run tests with coverage report (`coverage.html`) | | `make lint` | Run golangci-lint | | `make fmt` | Format code with `go fmt` | | `make dev` | Run with hot reload using air | | `make migrate-up` | Run database migrations | | `make migrate-down` | Rollback last migration | | `make migrate-create name=` | Create new migration | ### Running a Single Test ```bash # Run tests in a specific package go test -v ./internal/handler # Run a specific test function go test -v -run TestFunctionName ./internal/handler # Run tests matching a pattern go test -v -run "Test.*Handler" ./... ``` ### Environment Variables ```bash export DIFFDOWN_SESSION_SECRET="your-secret-here" export BASE_URL="http://127.0.0.1:8080" export PORT=8080 export DB_PATH="./diffdown.db" ``` --- ## Code Style Guidelines ### Go Conventions - **Go Version**: 1.22+ - **Module**: `github.com/limeleaf/diffdown` - **Formatting**: Use `go fmt` or `gofumports` - run before committing ### Imports Organize imports in three groups with blank lines between: 1. Standard library 2. Third-party packages 3. Internal packages ```go import ( "context" "encoding/json" "fmt" "log" "net/http" "time" "github.com/gorilla/sessions" "golang.org/x/crypto/bcrypt" "github.com/limeleaf/diffdown/internal/auth" "github.com/limeleaf/diffdown/internal/db" "github.com/limeleaf/diffdown/internal/model" ) ``` ### Naming Conventions - **Files**: `snake_case.go` (e.g., `auth.go`, `handler.go`) - **Types/Interfaces**: `PascalCase` (e.g., `Handler`, `User`, `DB`) - **Functions/Methods**: `PascalCase` exported, `camelCase` unexported - **Constants**: `PascalCase` for exported, `camelCase` for unexported - **Variables**: `camelCase`, prefer short names (`db`, `err`, `w`, `r`) ### Error Handling - Always handle errors explicitly; avoid bare `err != nil` checks - Log meaningful context with `log.Printf` before returning errors - Return user-friendly error messages in HTTP handlers - Use wrapped errors with `%w` for context: `fmt.Errorf("open db: %w", err)` ### HTTP Handlers Follow the existing pattern in `internal/handler/handler func (h *Handler) Handler.go`: ```goName(w http.ResponseWriter, r *http.Request) { // 1. Authenticate/authorize user := h.currentUser(r) if user == nil { http.Redirect(w, r, "/auth/login", http.StatusSeeOther) return } // 2. Extract request data data := r.FormValue("field") // 3. Process (call DB, external API, etc.) result, err := h.DB.Method(data) if err != nil { log.Printf("HandlerName: operation: %v", err) h.render(w, "page.html", PageData{Error: "Friendly message"}) return } // 4. Respond h.render(w, "page.html", PageData{Content: result}) } ``` ### Database - Use SQLite with WAL mode: `_journal_mode=WAL&_foreign_keys=on` - Use ULID for IDs: `db.NewID()` (uses `oklog/ulid/v2`) - Use migrations in `migrations/` directory (SQL files) - Apply migrations on startup: `database.Migrate()` ### Authentication - Sessions: Gorilla `sessions.CookieStore` - Passwords: bcrypt (via `golang.org/x/crypto/bcrypt`) - ATProto OAuth: DPoP tokens with JWT ### Testing - Tests should live in `*_test.go` files alongside the code they test - Use table-driven tests when testing multiple cases - Mock external dependencies (database, HTTP clients) where practical --- ## Project Structure ``` diffdown/ ├── cmd/ │ ├── server/main.go # Application entry point │ └── migrate/main.go # Migration tool ├── internal/ │ ├── atproto/ # ATProto OAuth, DPoP, XRPC client │ ├── auth/ # Session management, password hashing │ ├── db/ # Database operations, migrations │ ├── handler/ # HTTP handlers │ ├── middleware/ # HTTP middleware (logging, auth injection) │ ├── model/ # Data models │ └── render/ # Markdown rendering ├── migrations/ # SQL migration files ├── static/ # Frontend assets ├── templates/ # HTML templates ├── Makefile # Build commands └── go.mod # Go dependencies ``` --- ## Common Tasks ### Adding a New Handler 1. Add method to `Handler` struct in `internal/handler/handler.go` 2. Register route in `cmd/server/main.go` 3. Add template to `templates/` if needed ### Adding a Database Query 1. Add method to `DB` struct in `internal/db/db.go` 2. Follow naming: `GetX`, `CreateX`, `UpdateX`, `DeleteX` 3. Use `db.scanUser` helper for scanning rows ### Adding a Migration ```bash make migrate-create name=create_users_table ``` Then edit the generated SQL file in `migrations/`. --- ## Dependencies Key dependencies (see `go.mod`): - `github.com/gorilla/sessions` - Session management - `github.com/mattn/go-sqlite3` - SQLite driver - `github.com/yuin/goldmark` - Markdown parsing - `github.com/golang-jwt/jwt/v5` - JWT tokens - `golang.org/x/crypto` - bcrypt, cryptography --- ## Frontend (JavaScript) The frontend uses vanilla JavaScript with CodeMirror 6 for the editor. Bundling is done with esbuild: ```bash npm install npx esbuild --bundle --format=esm --outfile=static/vendor/codemirror.js ... ``` Static files are served from `static/` at `/static/`.