Files
tasksquire/CLAUDE.md
Martin Pander 02fa2e503a Add things
2026-02-04 13:13:04 +01:00

237 lines
7.3 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
TaskSquire is a Go-based Terminal User Interface (TUI) for Taskwarrior and Timewarrior. It uses the Bubble Tea framework (Model-View-Update pattern) from the Charm ecosystem.
**Key Technologies:**
- Go 1.22.2
- Bubble Tea (MVU pattern)
- Lip Gloss (styling)
- Huh (forms)
- Bubbles (components)
## Build and Development Commands
### Running and Building
```bash
# Run directly
go run main.go
# Build binary
go build -o tasksquire main.go
# Format code (always run before committing)
go fmt ./...
# Vet code
go vet ./...
# Tidy dependencies
go mod tidy
```
### Testing
```bash
# Run all tests
go test ./...
# Run tests for specific package
go test ./taskwarrior
# Run single test
go test ./taskwarrior -run TestTaskSquire_GetContext
# Run with verbose output
go test -v ./...
# Run with coverage
go test -cover ./...
```
### Linting
```bash
# Lint with golangci-lint (via nix-shell)
golangci-lint run
```
### Development Environment
The project uses Nix for development environment setup:
```bash
# Enter Nix development shell
nix develop
# Or use direnv (automatically loads .envrc)
direnv allow
```
## Architecture
### High-Level Structure
TaskSquire follows the MVU (Model-View-Update) pattern with a component-based architecture:
1. **Entry Point (`main.go`)**: Initializes TaskSquire and TimeSquire, creates Common state container, and starts Bubble Tea program
2. **Common State (`common/`)**: Shared state, components interface, keymaps, styles, and utilities
3. **Pages (`pages/`)**: Top-level UI views (report, taskEditor, timePage, pickers)
4. **Components (`components/`)**: Reusable UI widgets (input, table, timetable, picker)
5. **Business Logic**:
- `taskwarrior/`: Wraps Taskwarrior CLI, models, and config parsing
- `timewarrior/`: Wraps Timewarrior CLI, models, and config parsing
### Component System
All UI elements implement the `Component` interface:
```go
type Component interface {
tea.Model // Init(), Update(tea.Msg), View()
SetSize(width int, height int)
}
```
Components can be composed hierarchically. The `Common` struct manages a page stack for navigation.
### Page Navigation Pattern
- **Main Page** (`pages/main.go`): Root page with tab switching between Tasks and Time views
- **Page Stack**: Managed by `common.Common`, allows pushing/popping subpages
- `common.PushPage(page)` - push a new page on top
- `common.PopPage()` - return to previous page
- `common.HasSubpages()` - check if subpages are active
- **Tab Switching**: Only works at top level (when no subpages active)
### State Management
The `common.Common` struct acts as a shared state container:
- `TW`: TaskWarrior interface for task operations
- `TimeW`: TimeWarrior interface for time tracking
- `Keymap`: Centralized key bindings
- `Styles`: Centralized styling (parsed from Taskwarrior config)
- `Udas`: User Defined Attributes from Taskwarrior config
- `pageStack`: Stack-based page navigation
### Taskwarrior Integration
The `TaskWarrior` interface provides all task operations:
- Task CRUD: `GetTasks()`, `ImportTask()`, `SetTaskDone()`
- Task control: `StartTask()`, `StopTask()`, `DeleteTask()`
- Context management: `GetContext()`, `GetContexts()`, `SetContext()`
- Reports: `GetReport()`, `GetReports()`
- Config parsing: Manual parsing of Taskwarrior config format
All Taskwarrior operations use `exec.Command()` to call the `task` CLI binary. Results are parsed from JSON output.
### Timewarrior Integration
The `TimeWarrior` interface provides time tracking operations:
- Interval management: `GetIntervals()`, `ModifyInterval()`, `DeleteInterval()`
- Tracking control: `StartTracking()`, `StopTracking()`, `ContinueTracking()`
- Tag management: `GetTags()`, `GetTagCombinations()`
- Utility: `FillInterval()`, `JoinInterval()`, `Undo()`
Similar to TaskWarrior, uses `exec.Command()` to call the `timew` CLI binary.
### Custom JSON Marshaling
The `Task` struct uses custom `MarshalJSON()` and `UnmarshalJSON()` to handle:
- User Defined Attributes (UDAs) stored in `Udas map[string]any`
- Dynamic field handling via `json.RawMessage`
- Virtual tags (filtered from regular tags)
### Configuration and Environment
- **Taskwarrior Config**: Located via `TASKRC` env var, or fallback to `~/.taskrc` or `~/.config/task/taskrc`
- **Timewarrior Config**: Located via `TIMEWARRIORDB` env var, or fallback to `~/.timewarrior/timewarrior.cfg`
- **Config Parsing**: Custom parser in `taskwarrior/config.go` handles Taskwarrior's config format
- **Theme Colors**: Extracted from Taskwarrior config and used in Lip Gloss styles
### Concurrency
- Both `TaskSquire` and `TimeSquire` use `sync.Mutex` to protect shared state
- Lock pattern: `ts.mu.Lock()` followed by `defer ts.mu.Unlock()`
- Operations are synchronous (no goroutines in typical flows)
### Logging
- Uses `log/slog` for structured logging
- Logs written to `app.log` in current directory
- Errors logged but execution typically continues (graceful degradation)
- Log pattern: `slog.Error("message", "key", value)`
## Code Style and Patterns
### Import Organization
Standard library first, then third-party, then local:
```go
import (
"context"
"fmt"
tea "github.com/charmbracelet/bubbletea"
"tasksquire/common"
"tasksquire/taskwarrior"
)
```
### Naming Conventions
- Exported types: `PascalCase` (e.g., `TaskSquire`, `ReportPage`)
- Unexported fields: `camelCase` (e.g., `configLocation`, `activeReport`)
- Interfaces: Often end in 'er' or describe capability (e.g., `TaskWarrior`, `TimeWarrior`, `Component`)
### Error Handling
- Log errors with `slog.Error()` and continue execution
- Don't panic unless fatal initialization error
- Return errors from functions, don't log and return
### MVU Pattern in Bubble Tea
Components follow the MVU pattern:
- `Init() tea.Cmd`: Initialize and return commands for side effects
- `Update(tea.Msg) (tea.Model, tea.Cmd)`: Handle messages, update state, return commands
- `View() string`: Render UI as string
Custom messages for inter-component communication:
```go
type MyCustomMsg struct {
data string
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case MyCustomMsg:
// Handle custom message
}
return m, nil
}
```
### Styling with Lip Gloss
- Centralized styles in `common/styles.go`
- Theme colors parsed from Taskwarrior config
- Create reusable style functions, not inline styles
### Testing Patterns
- Table-driven tests with struct slices
- Helper functions like `TaskWarriorTestSetup()`
- Use `t.TempDir()` for isolated test environments
- Include `prep func()` in test cases for setup
## Important Implementation Details
### Virtual Tags
Taskwarrior has virtual tags (ACTIVE, BLOCKED, etc.) that are filtered out from regular tags. See the `virtualTags` map in `taskwarrior/taskwarrior.go`.
### Non-Standard Reports
Some Taskwarrior reports require special handling (burndown, calendar, etc.). See `nonStandardReports` map.
### Timestamp Format
Taskwarrior uses ISO 8601 format: `20060102T150405Z` (defined as `dtformat` constant)
### Color Parsing
Custom color parsing from Taskwarrior config format in `common/styles.go`
### VSCode Debugging
Launch configuration available for remote debugging on port 43000 (see `.vscode/launch.json`)