6.4 KiB
6.4 KiB
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
# 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
# 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
# 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/<package>for internal imports
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:
TestFunctionNameorTestType_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]incommon/stack.go)
Error Handling
- Logging Over Panicking: Use
log/slogfor structured logging, typically continue execution - Error Returns: Return errors from functions, don't log and return
- Context: Errors are often logged with
slog.Error()orslog.Warn()and execution continues
// 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.Mutexto protect shared state (seeTaskSquire.mu) - Lock Pattern: Lock before operations, defer unlock
ts.mu.Lock()
defer ts.mu.Unlock()
Configuration and Environment
- Environment Variables: Respect
TASKRCandTIMEWARRIORDB - 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
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
// 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.RawMessagefor flexible field handling
Key Files to Reference
common/component.go- Component interface definitioncommon/common.go- Shared state containertaskwarrior/taskwarrior.go- TaskWarrior interface and implementationpages/main.go- Main page router patterntaskwarrior/models.go- Data model examples
Development Notes
- Logging: Application logs to
app.login current directory - Virtual Tags: Filter out Taskwarrior virtual tags (see
virtualTagsmap) - Color Parsing: Custom color parsing from Taskwarrior config format
- Debugging: VSCode launch.json configured for remote debugging on port 43000