Detailed changes
@@ -5,8 +5,7 @@
package cmd
import (
- "fmt"
-
+ "git.secluded.site/lune/cmd/task"
"github.com/spf13/cobra"
)
@@ -16,9 +15,12 @@ var doneCmd = &cobra.Command{
GroupID: "shortcuts",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
- // TODO: implement as task update --status completed
- fmt.Fprintf(cmd.OutOrStdout(), "Marking task %s as done (not yet implemented)\n", args[0])
+ _ = task.UpdateCmd.Flags().Set("status", "completed")
+
+ task.UpdateCmd.SetIn(cmd.InOrStdin())
+ task.UpdateCmd.SetOut(cmd.OutOrStdout())
+ task.UpdateCmd.SetErr(cmd.ErrOrStderr())
- return nil
+ return task.UpdateCmd.RunE(task.UpdateCmd, args)
},
}
@@ -7,7 +7,7 @@ package habit
import (
"fmt"
- "git.secluded.site/lune/internal/config"
+ "git.secluded.site/lune/internal/completion"
"github.com/spf13/cobra"
)
@@ -20,7 +20,7 @@ var TrackCmd = &cobra.Command{
KEY is the habit key from your config (not the raw Lunatask ID).
Tracks for today by default. Use --date to specify another date.`,
Args: cobra.ExactArgs(1),
- ValidArgsFunction: completeHabits,
+ ValidArgsFunction: completion.Habits,
RunE: func(cmd *cobra.Command, args []string) error {
date, _ := cmd.Flags().GetString("date")
if date == "" {
@@ -37,18 +37,3 @@ Tracks for today by default. Use --date to specify another date.`,
func init() {
TrackCmd.Flags().StringP("date", "d", "", "Date performed (natural language, default: today)")
}
-
-// completeHabits returns habit keys from config for shell completion.
-func completeHabits(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
- cfg, err := config.Load()
- if err != nil {
- return nil, cobra.ShellCompDirectiveError
- }
-
- keys := make([]string, len(cfg.Habits))
- for i, h := range cfg.Habits {
- keys[i] = h.Key
- }
-
- return keys, cobra.ShellCompDirectiveNoFileComp
-}
@@ -7,7 +7,7 @@ package note
import (
"fmt"
- "git.secluded.site/lune/internal/config"
+ "git.secluded.site/lune/internal/completion"
"github.com/spf13/cobra"
)
@@ -34,20 +34,5 @@ func init() {
AddCmd.Flags().StringP("notebook", "b", "", "Notebook key (from config)")
AddCmd.Flags().StringP("content", "c", "", "Note content (use - for stdin)")
- _ = AddCmd.RegisterFlagCompletionFunc("notebook", completeNotebooks)
-}
-
-// completeNotebooks returns notebook keys from config for shell completion.
-func completeNotebooks(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
- cfg, err := config.Load()
- if err != nil {
- return nil, cobra.ShellCompDirectiveError
- }
-
- keys := make([]string, len(cfg.Notebooks))
- for i, n := range cfg.Notebooks {
- keys[i] = n.Key
- }
-
- return keys, cobra.ShellCompDirectiveNoFileComp
+ _ = AddCmd.RegisterFlagCompletionFunc("notebook", completion.Notebooks)
}
@@ -5,10 +5,7 @@
package note
import (
- "bufio"
"fmt"
- "os"
- "strings"
"git.secluded.site/lune/internal/ui"
"git.secluded.site/lune/internal/validate"
@@ -27,14 +24,7 @@ var DeleteCmd = &cobra.Command{
force, _ := cmd.Flags().GetBool("force")
if !force {
- fmt.Fprintf(cmd.OutOrStderr(), "%s Delete note %s? [y/N] ",
- ui.Warning.Render("Warning:"), args[0])
-
- reader := bufio.NewReader(os.Stdin)
- response, _ := reader.ReadString('\n')
- response = strings.TrimSpace(strings.ToLower(response))
-
- if response != "y" && response != "yes" {
+ if !ui.Confirm(fmt.Sprintf("Delete note %s?", args[0])) {
fmt.Fprintln(cmd.OutOrStdout(), "Cancelled")
return nil
@@ -7,6 +7,7 @@ package note
import (
"fmt"
+ "git.secluded.site/lune/internal/completion"
"github.com/spf13/cobra"
)
@@ -30,5 +31,5 @@ func init() {
ListCmd.Flags().StringP("notebook", "b", "", "Filter by notebook key")
ListCmd.Flags().Bool("json", false, "Output as JSON")
- _ = ListCmd.RegisterFlagCompletionFunc("notebook", completeNotebooks)
+ _ = ListCmd.RegisterFlagCompletionFunc("notebook", completion.Notebooks)
}
@@ -7,6 +7,7 @@ package note
import (
"fmt"
+ "git.secluded.site/lune/internal/completion"
"git.secluded.site/lune/internal/validate"
"github.com/spf13/cobra"
)
@@ -34,5 +35,5 @@ func init() {
UpdateCmd.Flags().StringP("content", "c", "", "Note content (use - for stdin)")
UpdateCmd.Flags().StringP("date", "d", "", "Note date (natural language)")
- _ = UpdateCmd.RegisterFlagCompletionFunc("notebook", completeNotebooks)
+ _ = UpdateCmd.RegisterFlagCompletionFunc("notebook", completion.Notebooks)
}
@@ -7,6 +7,7 @@ package person
import (
"fmt"
+ "git.secluded.site/lune/internal/completion"
"github.com/spf13/cobra"
)
@@ -26,18 +27,5 @@ var AddCmd = &cobra.Command{
func init() {
AddCmd.Flags().StringP("relationship", "r", "", "Relationship strength")
- _ = AddCmd.RegisterFlagCompletionFunc("relationship", completeRelationships)
-}
-
-// completeRelationships returns relationship strength options for shell completion.
-func completeRelationships(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
- return []string{
- "family",
- "intimate-friends",
- "close-friends",
- "casual-friends",
- "acquaintances",
- "business-contacts",
- "almost-strangers",
- }, cobra.ShellCompDirectiveNoFileComp
+ _ = AddCmd.RegisterFlagCompletionFunc("relationship", completion.Relationships)
}
@@ -5,10 +5,7 @@
package person
import (
- "bufio"
"fmt"
- "os"
- "strings"
"git.secluded.site/lune/internal/ui"
"git.secluded.site/lune/internal/validate"
@@ -27,14 +24,7 @@ var DeleteCmd = &cobra.Command{
force, _ := cmd.Flags().GetBool("force")
if !force {
- fmt.Fprintf(cmd.OutOrStderr(), "%s Delete person %s? [y/N] ",
- ui.Warning.Render("Warning:"), args[0])
-
- reader := bufio.NewReader(os.Stdin)
- response, _ := reader.ReadString('\n')
- response = strings.TrimSpace(strings.ToLower(response))
-
- if response != "y" && response != "yes" {
+ if !ui.Confirm(fmt.Sprintf("Delete person %s?", args[0])) {
fmt.Fprintln(cmd.OutOrStdout(), "Cancelled")
return nil
@@ -7,6 +7,7 @@ package person
import (
"fmt"
+ "git.secluded.site/lune/internal/completion"
"git.secluded.site/lune/internal/validate"
"github.com/spf13/cobra"
)
@@ -33,5 +34,5 @@ func init() {
UpdateCmd.Flags().String("last", "", "Last name")
UpdateCmd.Flags().StringP("relationship", "r", "", "Relationship strength")
- _ = UpdateCmd.RegisterFlagCompletionFunc("relationship", completeRelationships)
+ _ = UpdateCmd.RegisterFlagCompletionFunc("relationship", completion.Relationships)
}
@@ -7,7 +7,7 @@ package task
import (
"fmt"
- "git.secluded.site/lune/internal/config"
+ "git.secluded.site/lune/internal/completion"
"github.com/spf13/cobra"
)
@@ -40,53 +40,12 @@ func init() {
AddCmd.Flags().Int("eisenhower", 0, "Eisenhower quadrant: 1-4")
AddCmd.Flags().String("schedule", "", "Schedule date (natural language)")
- _ = AddCmd.RegisterFlagCompletionFunc("area", completeAreas)
- _ = AddCmd.RegisterFlagCompletionFunc("goal", completeGoals)
+ _ = AddCmd.RegisterFlagCompletionFunc("area", completion.Areas)
+ _ = AddCmd.RegisterFlagCompletionFunc("goal", completion.Goals)
_ = AddCmd.RegisterFlagCompletionFunc("status",
- func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
- return []string{"later", "next", "started", "waiting"}, cobra.ShellCompDirectiveNoFileComp
- })
+ completion.Static("later", "next", "started", "waiting"))
_ = AddCmd.RegisterFlagCompletionFunc("motivation",
- func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
- return []string{"must", "should", "want"}, cobra.ShellCompDirectiveNoFileComp
- })
+ completion.Static("must", "should", "want"))
_ = AddCmd.RegisterFlagCompletionFunc("eisenhower",
- func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
- return []string{"1", "2", "3", "4"}, cobra.ShellCompDirectiveNoFileComp
- })
-}
-
-// completeAreas returns area keys from config for shell completion.
-func completeAreas(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
- cfg, err := config.Load()
- if err != nil {
- return nil, cobra.ShellCompDirectiveError
- }
-
- keys := make([]string, len(cfg.Areas))
- for i, a := range cfg.Areas {
- keys[i] = a.Key
- }
-
- return keys, cobra.ShellCompDirectiveNoFileComp
-}
-
-// completeGoals returns goal keys from config for shell completion.
-// Note: This returns all goals across all areas. A smarter completion
-// would filter based on the --area flag value if set.
-func completeGoals(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
- cfg, err := config.Load()
- if err != nil {
- return nil, cobra.ShellCompDirectiveError
- }
-
- var keys []string
-
- for _, a := range cfg.Areas {
- for _, g := range a.Goals {
- keys = append(keys, g.Key)
- }
- }
-
- return keys, cobra.ShellCompDirectiveNoFileComp
+ completion.Static("1", "2", "3", "4"))
}
@@ -5,10 +5,7 @@
package task
import (
- "bufio"
"fmt"
- "os"
- "strings"
"git.secluded.site/lune/internal/ui"
"git.secluded.site/lune/internal/validate"
@@ -27,14 +24,7 @@ var DeleteCmd = &cobra.Command{
force, _ := cmd.Flags().GetBool("force")
if !force {
- fmt.Fprintf(cmd.OutOrStderr(), "%s Delete task %s? [y/N] ",
- ui.Warning.Render("Warning:"), args[0])
-
- reader := bufio.NewReader(os.Stdin)
- response, _ := reader.ReadString('\n')
- response = strings.TrimSpace(strings.ToLower(response))
-
- if response != "y" && response != "yes" {
+ if !ui.Confirm(fmt.Sprintf("Delete task %s?", args[0])) {
fmt.Fprintln(cmd.OutOrStdout(), "Cancelled")
return nil
@@ -7,6 +7,7 @@ package task
import (
"fmt"
+ "git.secluded.site/lune/internal/completion"
"github.com/spf13/cobra"
)
@@ -31,9 +32,7 @@ func init() {
ListCmd.Flags().StringP("status", "s", "", "Filter by status")
ListCmd.Flags().Bool("json", false, "Output as JSON")
- _ = ListCmd.RegisterFlagCompletionFunc("area", completeAreas)
+ _ = ListCmd.RegisterFlagCompletionFunc("area", completion.Areas)
_ = ListCmd.RegisterFlagCompletionFunc("status",
- func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
- return []string{"later", "next", "started", "waiting", "completed"}, cobra.ShellCompDirectiveNoFileComp
- })
+ completion.Static("later", "next", "started", "waiting", "completed"))
}
@@ -7,6 +7,7 @@ package task
import (
"fmt"
+ "git.secluded.site/lune/internal/completion"
"git.secluded.site/lune/internal/validate"
"github.com/spf13/cobra"
)
@@ -40,14 +41,12 @@ func init() {
UpdateCmd.Flags().Int("eisenhower", 0, "Eisenhower quadrant: 1-4")
UpdateCmd.Flags().String("schedule", "", "Schedule date (natural language)")
- _ = UpdateCmd.RegisterFlagCompletionFunc("area", completeAreas)
- _ = UpdateCmd.RegisterFlagCompletionFunc("goal", completeGoals)
+ _ = UpdateCmd.RegisterFlagCompletionFunc("area", completion.Areas)
+ _ = UpdateCmd.RegisterFlagCompletionFunc("goal", completion.Goals)
_ = UpdateCmd.RegisterFlagCompletionFunc("status",
- func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
- return []string{"later", "next", "started", "waiting", "completed"}, cobra.ShellCompDirectiveNoFileComp
- })
+ completion.Static("later", "next", "started", "waiting", "completed"))
_ = UpdateCmd.RegisterFlagCompletionFunc("motivation",
- func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
- return []string{"must", "should", "want"}, cobra.ShellCompDirectiveNoFileComp
- })
+ completion.Static("must", "should", "want"))
+ _ = UpdateCmd.RegisterFlagCompletionFunc("eisenhower",
+ completion.Static("1", "2", "3", "4"))
}
@@ -13,22 +13,33 @@ require (
require (
charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410 // indirect
+ github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
+ github.com/catppuccin/go v0.3.0 // indirect
+ github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 // indirect
+ github.com/charmbracelet/bubbletea v1.3.6 // indirect
github.com/charmbracelet/colorprofile v0.4.1 // indirect
+ github.com/charmbracelet/huh v0.8.0 // indirect
github.com/charmbracelet/ultraviolet v0.0.0-20251217160852-6b0c0e26fad9 // indirect
github.com/charmbracelet/x/ansi v0.11.3 // indirect
github.com/charmbracelet/x/cellbuf v0.0.14 // indirect
github.com/charmbracelet/x/exp/charmtone v0.0.0-20251215102626-e0db08df7383 // indirect
+ github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect
github.com/charmbracelet/x/term v0.2.2 // indirect
github.com/charmbracelet/x/termios v0.1.1 // indirect
github.com/charmbracelet/x/windows v0.2.2 // indirect
github.com/clipperhouse/displaywidth v0.6.2 // indirect
github.com/clipperhouse/stringish v0.1.1 // indirect
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
+ github.com/dustin/go-humanize v1.0.1 // indirect
+ github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.19 // indirect
+ github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
+ github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/mango v0.2.0 // indirect
github.com/muesli/mango-cobra v1.3.0 // indirect
@@ -4,14 +4,24 @@ git.secluded.site/go-lunatask v0.1.0-rc7 h1:kzwAN9h4zVTo0OBs4B23ba5mAqxus6nYU62L
git.secluded.site/go-lunatask v0.1.0-rc7/go.mod h1:sWUQxme1z7qfsfS59nU5hqPvsRCt+HBmT/yBeIn6Fmc=
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
+github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
+github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY=
github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E=
+github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY=
+github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
+github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 h1:JFgG/xnwFfbezlUnFMJy0nusZvytYysV4SCS2cYbvws=
+github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7/go.mod h1:ISC1gtLcVilLOf23wvTfoQuYbW2q0JevFxPfUzZ9Ybw=
+github.com/charmbracelet/bubbletea v1.3.6 h1:VkHIxPJQeDt0aFJIsVxw8BQdh/F/L2KKZGsK6et5taU=
+github.com/charmbracelet/bubbletea v1.3.6/go.mod h1:oQD9VCRQFF8KplacJLo28/jofOI2ToOfGYeFgBBxHOc=
github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk=
github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk=
github.com/charmbracelet/fang v0.4.4 h1:G4qKxF6or/eTPgmAolwPuRNyuci3hTUGGX1rj1YkHJY=
github.com/charmbracelet/fang v0.4.4/go.mod h1:P5/DNb9DddQ0Z0dbc0P3ol4/ix5Po7Ofr2KMBfAqoCo=
+github.com/charmbracelet/huh v0.8.0 h1:Xz/Pm2h64cXQZn/Jvele4J3r7DDiqFCNIVteYukxDvY=
+github.com/charmbracelet/huh v0.8.0/go.mod h1:5YVc+SlZ1IhQALxRPpkGwwEKftN/+OlJlnJYlDRFqN4=
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
github.com/charmbracelet/ultraviolet v0.0.0-20251217160852-6b0c0e26fad9 h1:dsDBRP9Iyco0EjVpCsAzl8VGbxk04fP3sa80ySJSAZw=
@@ -24,6 +34,8 @@ github.com/charmbracelet/x/exp/charmtone v0.0.0-20251215102626-e0db08df7383 h1:x
github.com/charmbracelet/x/exp/charmtone v0.0.0-20251215102626-e0db08df7383/go.mod h1:nsExn0DGyX0lh9LwLHTn2Gg+hafdzfSXnC+QmEJTZFY=
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA=
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
+github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 h1:qko3AQ4gK1MTS/de7F5hPGx6/k1u0w4TeYmBFwzYVP4=
+github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ=
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
@@ -39,6 +51,10 @@ github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsV
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
+github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
+github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
+github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@@ -47,8 +63,14 @@ github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQ
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
+github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
+github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
+github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
+github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
+github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/mango v0.2.0 h1:iNNc0c5VLQ6fsMgAqGQofByNUBH2Q2nEbD6TaI+5yyQ=
@@ -80,6 +102,7 @@ golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
+golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
@@ -0,0 +1,87 @@
+// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+// Package completion provides shell completion helpers for CLI commands.
+package completion
+
+import (
+ "git.secluded.site/lune/internal/config"
+ "github.com/spf13/cobra"
+)
+
+// Areas returns area keys from config for shell completion.
+func Areas(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
+ cfg, err := config.Load()
+ if err != nil {
+ return nil, cobra.ShellCompDirectiveError
+ }
+
+ keys := make([]string, len(cfg.Areas))
+ for i, a := range cfg.Areas {
+ keys[i] = a.Key
+ }
+
+ return keys, cobra.ShellCompDirectiveNoFileComp
+}
+
+// Goals returns all goal keys across all areas for shell completion.
+func Goals(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
+ cfg, err := config.Load()
+ if err != nil {
+ return nil, cobra.ShellCompDirectiveError
+ }
+
+ var keys []string
+
+ for _, a := range cfg.Areas {
+ for _, g := range a.Goals {
+ keys = append(keys, g.Key)
+ }
+ }
+
+ return keys, cobra.ShellCompDirectiveNoFileComp
+}
+
+// Notebooks returns notebook keys from config for shell completion.
+func Notebooks(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
+ cfg, err := config.Load()
+ if err != nil {
+ return nil, cobra.ShellCompDirectiveError
+ }
+
+ keys := make([]string, len(cfg.Notebooks))
+ for i, n := range cfg.Notebooks {
+ keys[i] = n.Key
+ }
+
+ return keys, cobra.ShellCompDirectiveNoFileComp
+}
+
+// Habits returns habit keys from config for shell completion.
+func Habits(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
+ cfg, err := config.Load()
+ if err != nil {
+ return nil, cobra.ShellCompDirectiveError
+ }
+
+ keys := make([]string, len(cfg.Habits))
+ for i, h := range cfg.Habits {
+ keys[i] = h.Key
+ }
+
+ return keys, cobra.ShellCompDirectiveNoFileComp
+}
+
+// Relationships returns relationship strength options for shell completion.
+func Relationships(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
+ return []string{
+ "family",
+ "intimate-friends",
+ "close-friends",
+ "casual-friends",
+ "acquaintances",
+ "business-contacts",
+ "almost-strangers",
+ }, cobra.ShellCompDirectiveNoFileComp
+}
@@ -0,0 +1,16 @@
+// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+package completion
+
+import "github.com/spf13/cobra"
+
+// Static returns a completion function that provides fixed values.
+func Static(values ...string) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
+ vals := append([]string(nil), values...)
+
+ return func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
+ return vals, cobra.ShellCompDirectiveNoFileComp
+ }
+}
@@ -0,0 +1,26 @@
+// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+package ui
+
+import "github.com/charmbracelet/huh"
+
+// Confirm asks the user to confirm an action using an interactive prompt.
+// Returns false on cancellation or error (fail closed).
+func Confirm(title string) bool {
+ var confirmed bool
+
+ err := huh.NewConfirm().
+ Title(title).
+ Affirmative("Yes").
+ Negative("No").
+ Inline(true).
+ Value(&confirmed).
+ Run()
+ if err != nil {
+ return false
+ }
+
+ return confirmed
+}