diff --git a/cmd/init/areas.go b/cmd/init/areas.go index d6e034eb664e5339fd93622550db13de36911ce9..15cde476e239d4dce4b58fa1e004a9ff7c912a53 100644 --- a/cmd/init/areas.go +++ b/cmd/init/areas.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" + "git.secluded.site/go-lunatask" "github.com/charmbracelet/huh" "git.secluded.site/lune/internal/config" @@ -137,6 +138,8 @@ func editArea(existing *config.Area, cfg *config.Config) (*config.Area, error) { area := config.Area{} if existing != nil { area = *existing + } else { + area.Workflow = lunatask.WorkflowNowLater } err := runItemForm(&area.Name, &area.Key, &area.ID, itemFormConfig{ @@ -150,9 +153,37 @@ func editArea(existing *config.Area, cfg *config.Config) (*config.Area, error) { return nil, err } + if err := selectWorkflow(&area.Workflow); err != nil { + return nil, err + } + return &area, nil } +func selectWorkflow(selected *lunatask.Workflow) error { + options := make([]huh.Option[lunatask.Workflow], 0, len(lunatask.Workflows())) + for _, w := range lunatask.Workflows() { + label := fmt.Sprintf("%s - %s", w, w.Description()) + options = append(options, huh.NewOption(label, w)) + } + + err := huh.NewSelect[lunatask.Workflow](). + Title("Workflow"). + Description("Determines which task fields are relevant for this area"). + Options(options...). + Value(selected). + Run() + if err != nil { + if errors.Is(err, huh.ErrUserAborted) { + return errQuit + } + + return err + } + + return nil +} + func validateAreaKey(cfg *config.Config, existing *config.Area) func(string) error { return func(input string) error { if err := validateKeyFormat(input); err != nil { diff --git a/cmd/mcp/server.go b/cmd/mcp/server.go index e860c8a91d7bc569ae376b2d757d51d6f16f918a..3f6d96aaa8fe8e3a558d4b5cabc954e7fbbbf2be 100644 --- a/cmd/mcp/server.go +++ b/cmd/mcp/server.go @@ -56,10 +56,11 @@ func toAreaProviders(cfgAreas []config.Area) []shared.AreaProvider { for _, area := range cfgAreas { providers = append(providers, shared.AreaProvider{ - ID: area.ID, - Name: area.Name, - Key: area.Key, - Goals: shared.ToGoalProviders(area.Goals), + ID: area.ID, + Name: area.Name, + Key: area.Key, + Workflow: area.Workflow, + Goals: shared.ToGoalProviders(area.Goals), }) } diff --git a/go.mod b/go.mod index d01b0c01f77ccacec6cc39b3e99a6b67d11b9045..41f7a2ca92cf80800943af74e7093ebd98f041dc 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ module git.secluded.site/lune go 1.25.5 require ( - git.secluded.site/go-lunatask v0.1.0-rc9.1 + git.secluded.site/go-lunatask v0.1.0-rc9.2 github.com/BurntSushi/toml v1.6.0 github.com/charmbracelet/fang v0.4.4 github.com/charmbracelet/huh v0.8.0 diff --git a/go.sum b/go.sum index 0d5d2d714b6f3353fb7f4b9ada9604c2053e432b..b647a66f962867b3391f7606dc0cc83156bd7f14 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ al.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXy al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410 h1:D9PbaszZYpB4nj+d6HTWr1onlmlyuGVNfL9gAi8iB3k= charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410/go.mod h1:1qZyvvVCenJO2M1ac2mX0yyiIZJoZmDM4DG4s0udJkU= -git.secluded.site/go-lunatask v0.1.0-rc9.1 h1:6dJcP3P+2QraPQ/wfPjCWaXv2mr1B4lMvBuQCNZd1t8= -git.secluded.site/go-lunatask v0.1.0-rc9.1/go.mod h1:rxps7BBqF+BkY8VN5E7J9zSOzSbtZ1hDmLEOHxjTHZQ= +git.secluded.site/go-lunatask v0.1.0-rc9.2 h1:fk5fCGdHmKpwz5HPy/n/LURBNweJoRN2xSay36VgA7g= +git.secluded.site/go-lunatask v0.1.0-rc9.2/go.mod h1:rxps7BBqF+BkY8VN5E7J9zSOzSbtZ1hDmLEOHxjTHZQ= github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= diff --git a/internal/completion/completion.go b/internal/completion/completion.go index 9bab7ba7bd07121e33e0c7448e04b00fb94e12ee..99938cb5e3d06f052822abb546682b262ed22947 100644 --- a/internal/completion/completion.go +++ b/internal/completion/completion.go @@ -6,6 +6,7 @@ package completion import ( + "git.secluded.site/go-lunatask" "git.secluded.site/lune/internal/config" "github.com/spf13/cobra" ) @@ -90,3 +91,15 @@ func Relationships(_ *cobra.Command, _ []string, _ string) ([]string, cobra.Shel "almost-strangers", }, cobra.ShellCompDirectiveNoFileComp } + +// Workflows returns workflow options for shell completion. +func Workflows(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { + workflows := lunatask.Workflows() + keys := make([]string, len(workflows)) + + for i, w := range workflows { + keys[i] = string(w) + } + + return keys, cobra.ShellCompDirectiveNoFileComp +} diff --git a/internal/config/config.go b/internal/config/config.go index 88c7be3b5182333cc93d8c839e8d5bc3f06b77d7..e5b636bc64b637924d1075347f86fe311c0f8b0f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -11,6 +11,7 @@ import ( "os" "path/filepath" + "git.secluded.site/go-lunatask" "github.com/BurntSushi/toml" ) @@ -130,10 +131,11 @@ type Defaults struct { // //nolint:recvcheck // Value receivers for Keyed interface; pointer receiver for GoalByKey is intentional. type Area struct { - ID string `json:"id" toml:"id"` - Name string `json:"name" toml:"name"` - Key string `json:"key" toml:"key"` - Goals []Goal `json:"goals" toml:"goals"` + ID string `json:"id" toml:"id"` + Name string `json:"name" toml:"name"` + Key string `json:"key" toml:"key"` + Workflow lunatask.Workflow `json:"workflow" toml:"workflow"` + Goals []Goal `json:"goals" toml:"goals"` } // Goal represents a goal within an area. diff --git a/internal/mcp/resources/areas/handler.go b/internal/mcp/resources/areas/handler.go index 0d09bdf6dec4025a0c90d8ba356d9c4948db6f0a..9017302e4d5d7ccb65740077ff96c376ea45be6d 100644 --- a/internal/mcp/resources/areas/handler.go +++ b/internal/mcp/resources/areas/handler.go @@ -24,9 +24,16 @@ Each area represents a life domain (e.g., Work, Personal, Health) and contains: - id: UUID to use when creating tasks in this area - name: Human-readable area name - key: Short alias for CLI usage -- goals: List of goals within the area, each with id, name, and key +- workflow: Task management style (determines which fields are relevant) +- workflow_description: Human-readable explanation of the workflow +- valid_statuses: Task statuses valid for this workflow +- uses_motivation: Whether motivation field (must/should/want) is relevant +- uses_eisenhower: Whether eisenhower matrix is relevant +- uses_scheduling: Whether date scheduling is relevant +- uses_priority: Whether priority field is relevant +- goals: List of goals within the area -Use this resource to discover valid area and goal IDs before creating or updating tasks.` +Use workflow information to determine which task fields to set for each area.` // Handler handles area resource requests. type Handler struct { @@ -40,10 +47,17 @@ func NewHandler(areas []shared.AreaProvider) *Handler { // areaInfo represents an area in the resource response. type areaInfo struct { - ID string `json:"id"` - Name string `json:"name"` - Key string `json:"key"` - Goals []goalInfo `json:"goals"` + ID string `json:"id"` + Name string `json:"name"` + Key string `json:"key"` + Workflow string `json:"workflow"` + WorkflowDescription string `json:"workflow_description"` + ValidStatuses []string `json:"valid_statuses"` + UsesMotivation bool `json:"uses_motivation"` + UsesEisenhower bool `json:"uses_eisenhower"` + UsesScheduling bool `json:"uses_scheduling"` + UsesPriority bool `json:"uses_priority"` + Goals []goalInfo `json:"goals"` } // goalInfo represents a goal in the resource response. @@ -70,11 +84,23 @@ func (h *Handler) HandleRead( }) } + validStatuses := make([]string, 0, len(area.Workflow.ValidStatuses())) + for _, s := range area.Workflow.ValidStatuses() { + validStatuses = append(validStatuses, string(s)) + } + areasInfo = append(areasInfo, areaInfo{ - ID: area.ID, - Name: area.Name, - Key: area.Key, - Goals: goals, + ID: area.ID, + Name: area.Name, + Key: area.Key, + Workflow: string(area.Workflow), + WorkflowDescription: area.Workflow.Description(), + ValidStatuses: validStatuses, + UsesMotivation: area.Workflow.UsesMotivation(), + UsesEisenhower: area.Workflow.UsesEisenhower(), + UsesScheduling: area.Workflow.UsesScheduling(), + UsesPriority: area.Workflow.UsesPriority(), + Goals: goals, }) } diff --git a/internal/mcp/shared/types.go b/internal/mcp/shared/types.go index d509d09f44365bcd60d78701e9e704e7752036b4..ee8c12d6424548ec96768ad80c16ba7677a74be1 100644 --- a/internal/mcp/shared/types.go +++ b/internal/mcp/shared/types.go @@ -5,6 +5,8 @@ // Package shared provides common types and utilities for MCP resources and tools. package shared +import "git.secluded.site/go-lunatask" + // Keyed is an interface for types that have ID, Name, and Key fields. type Keyed interface { GetID() string @@ -14,10 +16,11 @@ type Keyed interface { // AreaProvider represents an area with its goals for MCP resources. type AreaProvider struct { - ID string - Name string - Key string - Goals []GoalProvider + ID string + Name string + Key string + Workflow lunatask.Workflow + Goals []GoalProvider } // GoalProvider represents a goal within an area.