Documentation
¶
Overview ¶
Package todo implements a Jira-like todo tracker for jujutsu repositories.
Todos are stored in a special orphan change (parented at root()) that is bookmarked as "incr/tasks". This allows todos to sync across workspaces without polluting the code history.
The public API mirrors the CLI commands:
- Create, Update, Start, Close, Finish, Reopen for todo lifecycle
- Show, List, Ready for querying
- DepAdd, DepTree for dependency management
Index ¶
- Constants
- Variables
- func AgeData(item Todo, now time.Time) (time.Duration, bool)
- func DurationData(item Todo, now time.Time) (time.Duration, bool)
- func GenerateID(title string, timestamp time.Time) string
- func PriorityName(p int) string
- func TodoTypeRank(t TodoType) int
- func UpdatedData(item Todo, now time.Time) (time.Duration, bool)
- func ValidateDependency(d *Dependency) error
- func ValidatePriority(priority int) error
- func ValidateTitle(title string) error
- func ValidateTodo(t *Todo) error
- type CreateOptions
- type DepTreeNode
- type Dependency
- type IDIndex
- type ListFilter
- type OpenOptions
- type Prompter
- type Snapshotter
- type Status
- type StdioPrompter
- type Store
- func (s *Store) Close(ids []string) ([]Todo, error)
- func (s *Store) Create(title string, opts CreateOptions) (*Todo, error)
- func (s *Store) Delete(ids []string, reason string) ([]Todo, error)
- func (s *Store) DepAdd(todoID, dependsOnID string) (*Dependency, error)
- func (s *Store) DepTree(id string) (*DepTreeNode, error)
- func (s *Store) Finish(ids []string) ([]Todo, error)
- func (s *Store) IDIndex() (IDIndex, error)
- func (s *Store) List(filter ListFilter) ([]Todo, error)
- func (s *Store) ListWithIndex(filter ListFilter) ([]Todo, IDIndex, error)
- func (s *Store) Merge(ids []string) ([]Todo, error)
- func (s *Store) MergeFailed(ids []string) ([]Todo, error)
- func (s *Store) Queue(ids []string) ([]Todo, error)
- func (s *Store) QueueForMerge(ids []string, jobID string) ([]Todo, error)
- func (s *Store) Ready(limit int) ([]Todo, error)
- func (s *Store) ReadyWithIndex(limit int) ([]Todo, IDIndex, error)
- func (s *Store) Release() error
- func (s *Store) Reopen(ids []string) ([]Todo, error)
- func (s *Store) Show(ids []string) ([]Todo, error)
- func (s *Store) Start(ids []string) ([]Todo, error)
- func (s *Store) Update(ids []string, opts UpdateOptions) ([]Todo, error)
- type Todo
- type TodoType
- type UpdateOptions
Constants ¶
const ( // BookmarkName is the jj bookmark used to identify the todo store change. BookmarkName = "incr/tasks" // TodosFile is the name of the JSONL file containing todos. TodosFile = "todos.jsonl" // DependenciesFile is the name of the JSONL file containing dependencies. DependenciesFile = "dependencies.jsonl" )
const ( PriorityCritical = 0 PriorityHigh = 1 PriorityMedium = 2 // default PriorityLow = 3 PriorityBacklog = 4 PriorityMin = 0 PriorityMax = 4 )
Priority constants for todos.
const MaxTitleLength = 500
MaxTitleLength is the maximum allowed length for a todo title.
Variables ¶
var ( // ErrEmptyTitle is returned when a todo title is empty. ErrEmptyTitle = errors.New("title cannot be empty") // ErrTitleTooLong is returned when a todo title exceeds MaxTitleLength. ErrTitleTooLong = errors.New("title exceeds maximum length") // ErrInvalidStatus is returned when an invalid status is provided. ErrInvalidStatus = errors.New("invalid status") // ErrInvalidPriority is returned when priority is outside valid range. ErrInvalidPriority = errors.New("priority must be between 0 and 4") // ErrInvalidType is returned when an invalid todo type is provided. ErrInvalidType = errors.New("invalid todo type") // ErrTodoNotFound is returned when a todo with the given ID doesn't exist. ErrTodoNotFound = errors.New("todo not found") // ErrAmbiguousTodoIDPrefix is returned when an ID prefix matches multiple todos. ErrAmbiguousTodoIDPrefix = errors.New("ambiguous todo ID prefix") // ErrSelfDependency is returned when trying to create a dependency on itself. ErrSelfDependency = errors.New("todo cannot depend on itself") // ErrEmptyDependencyTodoID is returned when a dependency lacks a todo ID. ErrEmptyDependencyTodoID = errors.New("todo_id cannot be empty") // ErrEmptyDependencyDependsOnID is returned when a dependency lacks a depends-on ID. ErrEmptyDependencyDependsOnID = errors.New("depends_on_id cannot be empty") // ErrDuplicateDependency is returned when the dependency already exists. ErrDuplicateDependency = errors.New("dependency already exists") // ErrNoTodoStore is returned when the todo store bookmark doesn't exist. ErrNoTodoStore = errors.New("no todo store found (bookmark incr/tasks does not exist)") // ErrReadOnlyStore is returned when attempting to write using a read-only store. ErrReadOnlyStore = errors.New("todo store opened read-only") // ErrClosedTodoMissingClosedAt is returned when a closed or done todo has no closed_at timestamp. ErrClosedTodoMissingClosedAt = errors.New("closed or done todo must have closed_at timestamp") // ErrNotClosedTodoHasClosedAt is returned when a non-closed todo has a closed_at timestamp. ErrNotClosedTodoHasClosedAt = errors.New("non-closed todo cannot have closed_at timestamp") // ErrDeleteReasonRequiresDeletedAt is returned when a delete reason is provided without deleted_at. ErrDeleteReasonRequiresDeletedAt = errors.New("delete reason requires deleted_at timestamp") // ErrTombstoneMissingDeletedAt is returned when a tombstone todo has no deleted_at timestamp. ErrTombstoneMissingDeletedAt = errors.New("tombstone todo must have deleted_at timestamp") // ErrDeletedAtRequiresTombstoneStatus is returned when deleted_at is set without tombstone status. ErrDeletedAtRequiresTombstoneStatus = errors.New("deleted_at requires tombstone status") // ErrDeleteReasonRequiresTombstoneStatus is returned when delete reason is set without tombstone status. ErrDeleteReasonRequiresTombstoneStatus = errors.New("delete reason requires tombstone status") // ErrTombstoneHasClosedAt is returned when a tombstone todo has a closed_at timestamp. ErrTombstoneHasClosedAt = errors.New("tombstone todo cannot have closed_at timestamp") // ErrStartedAtRequiresActiveStatus is returned when started_at is set for a non-active todo. ErrStartedAtRequiresActiveStatus = errors.New("started_at requires in_progress, queued_for_merge, merging, merge_failed, or done status") // ErrCompletedAtRequiresDoneStatus is returned when completed_at is set for a non-complete todo. ErrCompletedAtRequiresDoneStatus = errors.New("completed_at requires queued_for_merge, merging, merge_failed, or done status") // ErrMergeStatusMissingCompletedAt is returned when a merge status is missing completed_at. ErrMergeStatusMissingCompletedAt = errors.New("merge statuses require completed_at") // ErrJobIDRequiresMergeStatus is returned when job_id is set for a non-merge todo. ErrJobIDRequiresMergeStatus = errors.New("job_id requires queued_for_merge, merging, merge_failed, or done status") )
Functions ¶
func DurationData ¶
DurationData computes the display duration and whether timing data exists.
func GenerateID ¶
GenerateID creates a unique 8-character alphanumeric ID from a title and timestamp. The ID is derived from SHA-256 hash of the title concatenated with the timestamp.
func PriorityName ¶
PriorityName returns a human-readable name for the priority level.
func TodoTypeRank ¶
TodoTypeRank returns the sort rank for a todo type.
func UpdatedData ¶
UpdatedData computes the display updated age and whether timing data exists.
func ValidateDependency ¶
func ValidateDependency(d *Dependency) error
ValidateDependency checks if a dependency is valid.
func ValidatePriority ¶
ValidatePriority checks if the priority is valid.
func ValidateTitle ¶
ValidateTitle checks if the title is valid.
Types ¶
type CreateOptions ¶
type CreateOptions struct {
// Status is the todo status. Defaults to StatusOpen.
Status Status
// Type is the todo type (task, bug, feature, design). Defaults to TypeTask.
Type TodoType
// Priority is the importance level (0-4). Defaults to PriorityMedium (2) when nil.
Priority *int
// Description provides additional context.
Description string
// ImplementationModel selects the model for implementing.
ImplementationModel string
// CodeReviewModel selects the model for commit review.
CodeReviewModel string
// ProjectReviewModel selects the model for project review.
ProjectReviewModel string
// Dependencies is a list of dependency IDs.
Dependencies []string
}
CreateOptions configures a new todo.
type DepTreeNode ¶
type DepTreeNode struct {
// Todo is the todo at this node.
Todo *Todo
// Children are the todos that this todo depends on.
Children []*DepTreeNode
}
DepTreeNode represents a node in a dependency tree.
type Dependency ¶
type Dependency struct {
// TodoID is the todo that has the dependency.
TodoID string `json:"todo_id"`
// DependsOnID is the todo that TodoID depends on.
DependsOnID string `json:"depends_on_id"`
// CreatedAt is when the dependency was created.
CreatedAt time.Time `json:"created_at"`
}
Dependency represents a relationship between two todos.
type IDIndex ¶
type IDIndex struct {
// contains filtered or unexported fields
}
IDIndex indexes todo IDs for prefix matching and display.
func NewIDIndex ¶
NewIDIndex builds an IDIndex from a slice of todos.
func (IDIndex) PrefixLengths ¶
PrefixLengths returns the shortest unique prefix length for each ID.
type ListFilter ¶
type ListFilter struct {
// Status filters by exact status match.
Status *Status
// Priority filters by exact priority match.
Priority *int
// Type filters by exact type match.
Type *TodoType
// IDs filters to specific IDs.
IDs []string
// TitleSubstring filters to todos with this substring in the title.
TitleSubstring string
// DescriptionSubstring filters to todos with this substring in the description.
DescriptionSubstring string
// IncludeTombstones includes soft-deleted todos. Default is false.
IncludeTombstones bool
}
ListFilter configures which todos to return.
type OpenOptions ¶
type OpenOptions struct {
// Prompter is used for user confirmation. If nil, StdioPrompter is used.
Prompter Prompter
// CreateIfMissing creates the todo store if it doesn't exist.
// If false and the store doesn't exist, ErrNoTodoStore is returned.
CreateIfMissing bool
// PromptToCreate prompts the user before creating a new store.
// Only used when CreateIfMissing is true.
PromptToCreate bool
// Purpose describes why the store workspace is acquired.
// If empty, a default purpose is used.
Purpose string
// ReadOnly opens the store without acquiring a workspace.
// Read-only mode cannot create missing stores.
ReadOnly bool
}
OpenOptions configures how the store is opened.
type Prompter ¶
type Prompter interface {
// Confirm asks the user a yes/no question and returns true if they say yes.
Confirm(message string) (bool, error)
}
Prompter is used to ask the user for confirmation.
type Snapshotter ¶
Snapshotter records workspace changes.
type Status ¶
type Status string
Status represents the state of a todo.
const ( // StatusOpen indicates the todo is ready to be worked on. StatusOpen Status = "open" // StatusProposed indicates the todo is awaiting review before starting. StatusProposed Status = "proposed" // StatusQueued indicates the todo is queued for batch processing. // Used by `job do` when multiple todos are specified to show that work // on this todo is scheduled. The CLI is responsible for cleanup: // queued todos must be reset to open if the batch exits before processing them. StatusQueued Status = "queued" // StatusInProgress indicates the todo is currently being worked on. StatusInProgress Status = "in_progress" // StatusQueuedForMerge indicates implementation finished and the todo is queued for merging. StatusQueuedForMerge Status = "queued_for_merge" // StatusMerging indicates the todo is being merged onto the target bookmark. StatusMerging Status = "merging" // StatusMergeFailed indicates the merge failed and needs manual intervention. StatusMergeFailed Status = "merge_failed" // StatusClosed indicates the todo has been completed. StatusClosed Status = "closed" // StatusDone indicates the todo is finished without closing it. StatusDone Status = "done" // StatusWaiting indicates the todo is blocked on external factors. // Unlike dependency blocking (for internal task ordering), waiting is for // external factors like upstream PRs, API availability, etc. The reason // for waiting lives in the description field. StatusWaiting Status = "waiting" // StatusStuck indicates the todo has been marked as stuck. // Stuck todos are not considered ready and indicate that automated work // should not continue without human intervention. StatusStuck Status = "stuck" // StatusTombstone indicates the todo has been soft-deleted. StatusTombstone Status = "tombstone" )
func (Status) IsResolved ¶
IsResolved returns true when a status is considered resolved for dependencies.
type StdioPrompter ¶
type StdioPrompter struct{}
StdioPrompter implements Prompter using stdin for input and stderr for output.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store provides access to the todo data for a jujutsu repository. It manages workspace acquisition and file locking for concurrent access.
func Open ¶
func Open(repoPath string, opts OpenOptions) (*Store, error)
Open opens the todo store for the repository at repoPath. If the incr/tasks bookmark doesn't exist and PromptToCreate is true, the user will be prompted to create it.
func (*Store) Create ¶
func (s *Store) Create(title string, opts CreateOptions) (*Todo, error)
Create creates a new todo with the given title.
func (*Store) DepAdd ¶
func (s *Store) DepAdd(todoID, dependsOnID string) (*Dependency, error)
DepAdd adds a dependency between two todos.
func (*Store) DepTree ¶
func (s *Store) DepTree(id string) (*DepTreeNode, error)
DepTree returns the dependency tree for a todo.
func (*Store) List ¶
func (s *Store) List(filter ListFilter) ([]Todo, error)
List returns todos matching the filter.
func (*Store) ListWithIndex ¶
func (s *Store) ListWithIndex(filter ListFilter) ([]Todo, IDIndex, error)
ListWithIndex returns todos matching the filter plus a full ID index.
func (*Store) MergeFailed ¶
MergeFailed marks todos as failed merges.
func (*Store) QueueForMerge ¶
QueueForMerge marks todos as ready to merge and stores the job ID.
func (*Store) ReadyWithIndex ¶
ReadyWithIndex returns ready todos plus a full ID index.
func (*Store) Release ¶
Release releases the workspace back to the pool. This should be called when done using the store.
type Todo ¶
type Todo struct {
// ID is a unique identifier (8-char alphanumeric, derived from initial title + timestamp).
ID string `json:"id"`
// Title is the short summary of the todo (max 500 chars).
Title string `json:"title"`
// Description provides additional context about the todo.
Description string `json:"description"`
// Status is the current state of the todo.
Status Status `json:"status"`
// Priority is the importance level (0=critical, 4=backlog).
Priority int `json:"priority"`
// Type categorizes the todo (task, bug, feature, design).
Type TodoType `json:"type"`
// ImplementationModel selects the model for implementing this todo.
ImplementationModel string `json:"implementation_model,omitempty"`
// CodeReviewModel selects the model for commit review on this todo.
CodeReviewModel string `json:"code_review_model,omitempty"`
// ProjectReviewModel selects the model for final project review on this todo.
ProjectReviewModel string `json:"project_review_model,omitempty"`
// CreatedAt is when the todo was created.
CreatedAt time.Time `json:"created_at"`
// UpdatedAt is when the todo was last modified.
UpdatedAt time.Time `json:"updated_at"`
// ClosedAt is when the todo was closed or marked done (nil if not closed/done).
ClosedAt *time.Time `json:"closed_at,omitempty"`
// StartedAt is when the todo entered in_progress (nil when not tracking).
StartedAt *time.Time `json:"started_at,omitempty"`
// CompletedAt is when the todo completed (nil when not completed).
CompletedAt *time.Time `json:"completed_at,omitempty"`
// JobID is the ID of the job that produced the mergeable change.
JobID string `json:"job_id,omitempty"`
// DeletedAt is when the todo was soft-deleted (nil if not deleted).
DeletedAt *time.Time `json:"deleted_at,omitempty"`
// DeleteReason explains why the todo was deleted.
DeleteReason string `json:"delete_reason,omitempty"`
// Source tracks the origin of the todo.
// Empty or omitted means user-created. Values like "habit:<name>" indicate
// the todo was created by running a habit.
Source string `json:"source,omitempty"`
}
Todo represents a single task.
type TodoType ¶
type TodoType string
TodoType represents the category of a todo.
const ( // TypeTask is a general task (default). TypeTask TodoType = "task" // TypeBug is a bug fix. TypeBug TodoType = "bug" // TypeFeature is a new feature. TypeFeature TodoType = "feature" // TypeDesign is a design or specification task requiring interactive work. TypeDesign TodoType = "design" )
func ValidTodoTypes ¶
func ValidTodoTypes() []TodoType
ValidTodoTypes returns all valid todo type values.
func (TodoType) IsInteractive ¶
IsInteractive returns true for todo types that require interactive sessions. Design todos produce specifications and require user collaboration.
type UpdateOptions ¶
type UpdateOptions struct {
Title *string
Description *string
Status *Status
Priority *int
Type *TodoType
ImplementationModel *string
CodeReviewModel *string
ProjectReviewModel *string
DeletedAt *time.Time
DeleteReason *string
Source *string
StartedAt *time.Time
CompletedAt *time.Time
JobID *string
}
UpdateOptions configures fields to update on todos. Nil pointers mean "don't update this field".