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

222 lines
6.2 KiB
Markdown

# 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
```bash
# 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
```bash
# 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
```bash
# 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
```bash
# 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](https://go.dev/doc/effective_go) and [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments)
- 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)
```go
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
```go
// 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
```go
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:
```go
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
```go
// 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.