my-log/AGENTS.md
2026-02-28 20:12:36 -06:00

6.2 KiB

AGENTS.md - Development Guidelines for my-log-wynter

Project Overview

my-log-wynter is a Go CLI application that extends the codeberg.org/danjones000/my-log CLI library with custom commands. It integrates with youtube-dl/yt-dlp for video metadata fetching. It uses and github.com/lrstanley/go-ytdlp for yt-dlp integration.

Build/Lint/Test Commands

Building

# Build all packages
go build ./...

# Build the CLI binary
go build -o my-log ./cmd/my-log

# Run the CLI
go run ./cmd/my-log

Testing

# Run all tests
go test ./...

# Run a single test by name
go test -run <TestName> ./...

# Run tests with verbose output
go test -v ./...

# Run tests with coverage
go test -cover ./...

# Run benchmarks
go test -bench=. ./...

Linting

# Run golangci-lint (comprehensive linter)
golangci-lint run ./...

# Run golangci-lint with auto-fix
golangci-lint run ./... --fix

# Run go vet
go vet ./...

# Format code (gofmt)
gofmt -w .
gofmt -d .

# Check for unused imports and other issues
goimports -w .

Dependencies

# Update dependencies
go get -u ./...

# Tidy go.mod
go mod tidy

# List dependencies
go list -m all

Code Style Guidelines

General Conventions

  • Follow Effective Go and Go Code Review Comments
  • Use Go 1.26.0 or later (as specified in go.mod)
  • Run go fmt and gofmt before committing
  • Run golangci-lint run ./... before committing

Naming Conventions

  • Packages: Use short, lowercase names (e.g., ytdlp, not yt-dlp or youtube_dlp)
  • Variables/Functions: Use camelCase (e.g., fetchURL, not fetch_url)
  • Exported Functions/Types: Use PascalCase (e.g., Fetch, not fetch)
  • Constants: Use PascalCase (e.g., MaxRetries) or camelCase for unexported (e.g., maxRetries)
  • Acronyms: Keep original casing (e.g., URL, not Url; ID, not Id)
  • Files: Use lowercase with underscores for multiple words (e.g., fetch_video.go)

Import Organization

Imports should be organized in three groups separated by blank lines:

  1. Standard library packages
  2. External/third-party packages
  3. Internal packages (if applicable)
import (
    "context"
    "fmt"
    "os"

    "codeberg.org/danjones000/my-log/cli"
    "github.com/lrstanley/go-ytdlp"
)

Error Handling

  • Return errors where possible; avoid panics except for truly unrecoverable conditions
  • Wrap errors with context using fmt.Errorf("description: %w", err) or %v
  • Handle errors explicitly; don't ignore them with _
  • Place error checks immediately after the operation that can fail
// Good
result, err := fetchURL(ctx, url)
if err != nil {
    return nil, fmt.Errorf("failed to fetch %s: %w", url, err)
}

// Avoid
result, _ := fetchURL(ctx, url) // Don't ignore errors

Context Usage

  • Pass context.Context as the first parameter for functions that may timeout or be cancelled
  • Use context.Background() for top-level operations and context.WithTimeout() for bounded operations
  • Check for context cancellation with ctx.Err() when appropriate
func Fetch(ctx context.Context, url string) (*Result, error) {
    // ...
}

Types and Interfaces

  • Define interfaces close to where they are used
  • Prefer concrete types unless interface polymorphism is needed
  • Use pointer receivers (*T) for methods that modify the receiver or when nil is meaningful

Testing Conventions

  • Test files should be named *_test.go
  • Test functions should start with Test (e.g., TestFetch)
  • Benchmark functions should start with Benchmark (e.g., BenchmarkFetch)
  • Use the github.com/nalgeon/be for assertions: be.Equal, be.Err, and be.True
  • Use table-driven tests when testing multiple cases:
func TestFetch(t *testing.T) {
    tests := []struct {
        name    string
        url     string
        wantErr bool
    }{
        {"valid youtube", "https://youtube.com/watch?v=xxx", false},
        {"invalid url", "not-a-url", true},
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            // test logic
            be.Equal(t, 2*5, 10)
            be.True(t, !false)
            be.Err(t, err, nil) // No error
            be.Err(t, ioErr, io.EOF) // ioErr should be an io.EOF
            be.Err(t, otherErr, "bad stuff") // error message contains "bad stuff"
        })
    }
}

Concurrency

  • Use goroutines with go keyword for concurrent operations
  • Use sync.WaitGroup or channels for synchronization
  • Prefer errgroup for coordinating multiple goroutines with error handling
  • Never leak goroutines; ensure they can complete or be cancelled

Documentation

  • Document exported functions, types, and constants with doc comments
  • Comments should start with the identifier name (no need to repeat the name)
  • Keep comments concise but explanatory
// Fetch retrieves video metadata from yt-dlp compatible sources.
func Fetch(ctx context.Context, url string) (*Info, error) {
    // ...
}

Logging

  • Print user-friendly messages to stderr

Project Structure

my-log-wynter/
├── cmd/my-log/          # CLI entry point
│   └── main.go
├── ytdlp/               # yt-dlp integration package
│   └── fetch.go
├── go.mod
├── go.sum
└── AGENTS.md            # This file

Common Tasks

Adding a new command

  1. Add the command to the my-log library (this project imports it)
  2. Update this repository's code as needed

Adding a new yt-dlp feature

  1. Modify ytdlp/fetch.go
  2. Add tests in ytdlp/fetch_test.go
  3. Run tests with go test -v ./ytdlp/

Pre-commit Checklist

  • Code is formatted: gofmt -w .
  • Imports are organized: goimports -w .
  • No lint errors: golangci-lint run ./...
  • Tests pass: go test ./...
  • Code builds: go build ./...

Git Commit Guidelines

  • Format: Prepend commit messages with a gitmoji emoji (see https://gitmoji.dev)
  • Style: Write detailed commit messages that explain what changed and why
  • Examples: ✨ Add JSON export functionality for log entries, 🐛 Fix date parsing for RFC3339 timestamps, 📝 Update README with configuration examples
  • Do not commit files not yet staged unless explicitly asked to do so.