208 lines
6.4 KiB
Markdown
208 lines
6.4 KiB
Markdown
# 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/<package>` 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
|