# Agent Development Guide for TaskSquire This guide is for AI coding agents working on TaskSquire, a Go-based TUI (Terminal User Interface) for Taskwarrior. ## Project Overview - **Language**: Go 1.22.2 - **Architecture**: Model-View-Update (MVU) pattern using Bubble Tea framework - **Module**: `tasksquire` - **Main Dependencies**: Bubble Tea, Lip Gloss, Huh, Bubbles (Charm ecosystem) ## Build, Test, and Lint Commands ### Building and Running ```bash # Run directly go run main.go # Build binary go build -o tasksquire main.go # Run tests go test ./... # Run tests for a specific package go test ./taskwarrior # Run a single test go test ./taskwarrior -run TestTaskSquire_GetContext # Run tests with verbose output go test -v ./taskwarrior # Run tests with coverage go test -cover ./... ``` ### Linting and Formatting ```bash # Format code (always run before committing) go fmt ./... # Lint with golangci-lint (available via nix-shell) golangci-lint run # Vet code for suspicious constructs go vet ./... # Tidy dependencies go mod tidy ``` ### Development Environment ```bash # Enter Nix development shell (provides all tools) nix develop # Or use direnv (automatically loads .envrc) direnv allow ``` ## Project Structure ``` tasksquire/ ├── main.go # Entry point: initializes TaskSquire, TimeSquire, and Bubble Tea ├── common/ # Shared state, components interface, keymaps, styles, utilities ├── pages/ # UI pages/views (report, taskEditor, timePage, pickers, etc.) ├── components/ # Reusable UI components (input, table, timetable, picker) ├── taskwarrior/ # Taskwarrior CLI wrapper, models, config ├── timewarrior/ # Timewarrior integration, models, config └── test/ # Test fixtures and data ``` ## Code Style Guidelines ### Imports - **Standard Library First**: Group standard library imports, then third-party, then local - **Local Import Pattern**: Use `tasksquire/` for internal imports ```go import ( "context" "fmt" "log/slog" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "tasksquire/common" "tasksquire/taskwarrior" ) ``` ### Naming Conventions - **Exported Types**: PascalCase (e.g., `TaskSquire`, `ReportPage`, `Common`) - **Unexported Fields**: camelCase (e.g., `configLocation`, `activeReport`, `pageStack`) - **Interfaces**: Follow Go convention, often ending in 'er' (e.g., `TaskWarrior`, `TimeWarrior`, `Component`) - **Constants**: PascalCase or SCREAMING_SNAKE_CASE for exported constants - **Test Functions**: `TestFunctionName` or `TestType_Method` ### Types and Interfaces - **Interface-Based Design**: Use interfaces for main abstractions (see `TaskWarrior`, `TimeWarrior`, `Component`) - **Struct Composition**: Embed common state (e.g., pages embed or reference `*common.Common`) - **Pointer Receivers**: Use pointer receivers for methods that modify state or for consistency - **Generic Types**: Use generics where appropriate (e.g., `Stack[T]` in `common/stack.go`) ### Error Handling - **Logging Over Panicking**: Use `log/slog` for structured logging, typically continue execution - **Error Returns**: Return errors from functions, don't log and return - **Context**: Errors are often logged with `slog.Error()` or `slog.Warn()` and execution continues ```go // Typical pattern if err != nil { slog.Error("Failed to get tasks", "error", err) return nil // or continue with default behavior } ``` ### Concurrency and Thread Safety - **Mutex Protection**: Use `sync.Mutex` to protect shared state (see `TaskSquire.mu`) - **Lock Pattern**: Lock before operations, defer unlock ```go ts.mu.Lock() defer ts.mu.Unlock() ``` ### Configuration and Environment - **Environment Variables**: Respect `TASKRC` and `TIMEWARRIORDB` - **Fallback Paths**: Check standard locations (`~/.taskrc`, `~/.config/task/taskrc`) - **Config Parsing**: Parse Taskwarrior config format manually (see `taskwarrior/config.go`) ### MVU Pattern (Bubble Tea) - **Components Implement**: `Init() tea.Cmd`, `Update(tea.Msg) (tea.Model, tea.Cmd)`, `View() string` - **Custom Messages**: Define custom message types for inter-component communication - **Cmd Chaining**: Return commands from Init/Update to trigger async operations ```go type MyMsg struct { data string } func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case MyMsg: // Handle custom message return m, nil } return m, nil } ``` ### Styling with Lip Gloss - **Centralized Styles**: Define styles in `common/styles.go` - **Theme Colors**: Parse colors from Taskwarrior config - **Reusable Styles**: Create style functions, not inline styles ### Testing - **Table-Driven Tests**: Use struct slices for test cases - **Test Setup**: Create helper functions like `TaskWarriorTestSetup()` - **Temp Directories**: Use `t.TempDir()` for isolated test environments - **Prep Functions**: Include `prep func()` in test cases for setup ### Documentation - **TODO Comments**: Mark future improvements with `// TODO: description` - **Package Comments**: Document package purpose at the top of main files - **Exported Functions**: Document exported functions, types, and methods ## Common Patterns ### Page Navigation - Pages pushed onto stack via `common.PushPage()` - Pop pages with `common.PopPage()` - Check for subpages with `common.HasSubpages()` ### Task Operations ```go // Get tasks for a report tasks := ts.GetTasks(report, "filter", "args") // Import/create task ts.ImportTask(&task) // Mark task done ts.SetTaskDone(&task) // Start/stop task ts.StartTask(&task) ts.StopTask(&task) ``` ### JSON Handling - Custom Marshal/Unmarshal for Task struct to handle UDAs (User Defined Attributes) - Use `json.RawMessage` for flexible field handling ## Key Files to Reference - `common/component.go` - Component interface definition - `common/common.go` - Shared state container - `taskwarrior/taskwarrior.go` - TaskWarrior interface and implementation - `pages/main.go` - Main page router pattern - `taskwarrior/models.go` - Data model examples ## Development Notes - **Logging**: Application logs to `app.log` in current directory - **Virtual Tags**: Filter out Taskwarrior virtual tags (see `virtualTags` map) - **Color Parsing**: Custom color parsing from Taskwarrior config format - **Debugging**: VSCode launch.json configured for remote debugging on port 43000