7.3 KiB
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
# 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
# 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
# Lint with golangci-lint (via nix-shell)
golangci-lint run
Development Environment
The project uses Nix for development environment setup:
# 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:
- Entry Point (
main.go): Initializes TaskSquire and TimeSquire, creates Common state container, and starts Bubble Tea program - Common State (
common/): Shared state, components interface, keymaps, styles, and utilities - Pages (
pages/): Top-level UI views (report, taskEditor, timePage, pickers) - Components (
components/): Reusable UI widgets (input, table, timetable, picker) - Business Logic:
taskwarrior/: Wraps Taskwarrior CLI, models, and config parsingtimewarrior/: Wraps Timewarrior CLI, models, and config parsing
Component System
All UI elements implement the Component interface:
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 subpagescommon.PushPage(page)- push a new page on topcommon.PopPage()- return to previous pagecommon.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 operationsTimeW: TimeWarrior interface for time trackingKeymap: Centralized key bindingsStyles: Centralized styling (parsed from Taskwarrior config)Udas: User Defined Attributes from Taskwarrior configpageStack: 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
TASKRCenv var, or fallback to~/.taskrcor~/.config/task/taskrc - Timewarrior Config: Located via
TIMEWARRIORDBenv var, or fallback to~/.timewarrior/timewarrior.cfg - Config Parsing: Custom parser in
taskwarrior/config.gohandles Taskwarrior's config format - Theme Colors: Extracted from Taskwarrior config and used in Lip Gloss styles
Concurrency
- Both
TaskSquireandTimeSquireusesync.Mutexto protect shared state - Lock pattern:
ts.mu.Lock()followed bydefer ts.mu.Unlock() - Operations are synchronous (no goroutines in typical flows)
Logging
- Uses
log/slogfor structured logging - Logs written to
app.login 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:
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 effectsUpdate(tea.Msg) (tea.Model, tea.Cmd): Handle messages, update state, return commandsView() string: Render UI as string
Custom messages for inter-component communication:
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)