237 lines
7.3 KiB
Markdown
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`)
|