dashboard / amolith/lune / listing real name of tasks #124 rss

open · opened on 2026-04-22T02:46:44Z by blocked
Help
checkout latest patchset:
ssh pr.pico.sh print pr-124 | git am -3
checkout any patchset in a patch request:
ssh pr.pico.sh print ps-X | git am -3
add changes to patch request:
git format-patch main --stdout | ssh pr.pico.sh pr add 124
add review to patch request:
git format-patch main --stdout | ssh pr.pico.sh pr add --review 124
accept PR:
ssh pr.pico.sh pr accept 124
close PR:
ssh pr.pico.sh pr close 124
Timeline Patchsets

Patchset ps-216

fix UTF8 chracteres

juan
2026-04-21T21:13:24Z

update help

juan
2026-04-21T21:21:36Z

list areas names

juan
2026-04-21T21:44:53Z

helper for TUI update task

juan
2026-04-21T23:49:56Z
Back to top
+25 -0 cmd/init/apikey.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
diff --git a/cmd/init/apikey.go b/cmd/init/apikey.go
index bb5b455..c306f9c 100644
--- a/cmd/init/apikey.go
+++ b/cmd/init/apikey.go
@@ -16,6 +16,7 @@ import (
 	"github.com/spf13/cobra"
 
 	"git.secluded.site/lune/internal/client"
+	"git.secluded.site/lune/internal/db"
 	"git.secluded.site/lune/internal/ui"
 )
 
@@ -219,3 +220,27 @@ func validateTokenWithPing(token string) error {
 
 	return err
 }
+
+func runMasterPasswordStep(cmd *cobra.Command) wizardNav {
+	var password string
+	err := huh.NewInput().
+		Title("Lunatask Master Password").
+		Description("Optional: provide your master password to enable local database decryption (stored securely).").
+		EchoMode(huh.EchoModePassword).
+		Value(&password).
+		Run()
+	if err != nil {
+		if errors.Is(err, huh.ErrUserAborted) {
+			return navQuit
+		}
+		return navNext // Continue anyway
+	}
+
+	if password != "" {
+		if err := db.SetMasterPassword(password); err != nil {
+			fmt.Fprintln(cmd.OutOrStdout(), ui.Error.Render("Failed to save master password: "+err.Error()))
+		}
+	}
+
+	return navNext
+}
+3 -0 cmd/init/init.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
diff --git a/cmd/init/init.go b/cmd/init/init.go
index 5aae718..e9bd37e 100644
--- a/cmd/init/init.go
+++ b/cmd/init/init.go
@@ -120,6 +120,7 @@ func runFreshSetup(cmd *cobra.Command, cfg *config.Config) error {
 		func() wizardNav { return runHabitsStep(cfg) },
 		func() wizardNav { return runDefaultsStep(cfg) },
 		func() wizardNav { return runAccessTokenStep(cmd) },
+		func() wizardNav { return runMasterPasswordStep(cmd) },
 	}
 
 	step := 0
@@ -173,6 +174,7 @@ func runReconfigure(cmd *cobra.Command, cfg *config.Config) error {
 		"defaults":  func() error { return configureDefaults(cfg) },
 		"ui":        func() error { return configureUIPrefs(cfg) },
 		"apikey":    func() error { return configureAccessToken(cmd) },
+		"password":  func() error { runMasterPasswordStep(cmd); return nil },
 		"reset":     func() error { return resetConfig(cmd, cfg) },
 	}
 
@@ -188,6 +190,7 @@ func runReconfigure(cmd *cobra.Command, cfg *config.Config) error {
 				huh.NewOption("Set defaults", "defaults"),
 				huh.NewOption("UI preferences", "ui"),
 				huh.NewOption("Access token", "apikey"),
+				huh.NewOption("Master password (for LocalDB)", "password"),
 				huh.NewOption("Reset all configuration", "reset"),
 				huh.NewOption("Done", choiceDone),
 			).
+34 -21 cmd/note/list.go link
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
diff --git a/cmd/note/list.go b/cmd/note/list.go
index 401e7c0..99783a0 100644
--- a/cmd/note/list.go
+++ b/cmd/note/list.go
@@ -13,12 +13,18 @@ import (
 	"git.secluded.site/lune/internal/client"
 	"git.secluded.site/lune/internal/completion"
 	"git.secluded.site/lune/internal/config"
+	"git.secluded.site/lune/internal/db"
 	"git.secluded.site/lune/internal/ui"
 	"github.com/charmbracelet/lipgloss"
 	"github.com/charmbracelet/lipgloss/table"
 	"github.com/spf13/cobra"
 )
 
+type enrichedNote struct {
+	lunatask.Note
+	Name string `json:"name,omitempty"`
+}
+
 // ListCmd lists notes. Exported for potential use by shortcuts.
 var ListCmd = &cobra.Command{
 	Use:   "list",
@@ -54,26 +60,42 @@ func runList(cmd *cobra.Command, _ []string) error {
 		return err
 	}
 
+	cfg, _ := config.Load()
+	enriched := make([]enrichedNote, 0, len(notes))
+	for _, n := range notes {
+		name := ""
+		if cfg != nil && cfg.Experimental.LocalDB {
+			name, _ = db.EnrichTask(n.ID) // EnrichTask is generic enough for IDs
+		}
+		enriched = append(enriched, enrichedNote{Note: n, Name: name})
+	}
+
 	notebookID, err := resolveNotebookFilter(cmd)
 	if err != nil {
 		return err
 	}
 
+	filtered := enriched
 	if notebookID != "" {
-		notes = filterByNotebook(notes, notebookID)
+		filtered = make([]enrichedNote, 0)
+		for _, en := range enriched {
+			if en.NotebookID != nil && *en.NotebookID == notebookID {
+				filtered = append(filtered, en)
+			}
+		}
 	}
 
-	if len(notes) == 0 {
+	if len(filtered) == 0 {
 		fmt.Fprintln(cmd.OutOrStdout(), "No notes found")
 
 		return nil
 	}
 
 	if mustGetBoolFlag(cmd, "json") {
-		return outputJSON(cmd, notes)
+		return outputJSONEnriched(cmd, filtered)
 	}
 
-	return outputTable(cmd, notes)
+	return outputTableEnriched(cmd, filtered)
 }
 
 func buildListOptions(cmd *cobra.Command) *lunatask.ListNotesOptions {
@@ -119,18 +141,6 @@ func resolveNotebookFilter(cmd *cobra.Command) (string, error) {
 	return notebook.ID, nil
 }
 
-func filterByNotebook(notes []lunatask.Note, notebookID string) []lunatask.Note {
-	filtered := make([]lunatask.Note, 0, len(notes))
-
-	for _, note := range notes {
-		if note.NotebookID != nil && *note.NotebookID == notebookID {
-			filtered = append(filtered, note)
-		}
-	}
-
-	return filtered
-}
-
 func mustGetStringFlag(cmd *cobra.Command, name string) string {
 	f := cmd.Flags().Lookup(name)
 	if f == nil {
@@ -149,7 +159,7 @@ func mustGetBoolFlag(cmd *cobra.Command, name string) bool {
 	return f.Value.String() == "true"
 }
 
-func outputJSON(cmd *cobra.Command, notes []lunatask.Note) error {
+func outputJSONEnriched(cmd *cobra.Command, notes []enrichedNote) error {
 	enc := json.NewEncoder(cmd.OutOrStdout())
 	enc.SetIndent("", "  ")
 
@@ -160,7 +170,7 @@ func outputJSON(cmd *cobra.Command, notes []lunatask.Note) error {
 	return nil
 }
 
-func outputTable(cmd *cobra.Command, notes []lunatask.Note) error {
+func outputTableEnriched(cmd *cobra.Command, notes []enrichedNote) error {
 	cfg, _ := config.Load()
 	rows := make([][]string, 0, len(notes))
 
@@ -185,13 +195,16 @@ func outputTable(cmd *cobra.Command, notes []lunatask.Note) error {
 			pinned = "📌"
 		}
 
-		created := ui.FormatDate(note.CreatedAt)
+		name := note.Name
+		if name == "" {
+			name = note.ID[:8] + "..."
+		}
 
-		rows = append(rows, []string{note.ID, notebook, dateOn, pinned, created})
+		rows = append(rows, []string{name, notebook, dateOn, pinned})
 	}
 
 	tbl := table.New().
-		Headers("ID", "NOTEBOOK", "DATE", "📌", "CREATED").
+		Headers("NAME", "NOTEBOOK", "DATE", "📌").
 		Rows(rows...).
 		StyleFunc(func(row, col int) lipgloss.Style {
 			if row == table.HeaderRow {
+48 -13 cmd/task/list.go link
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
diff --git a/cmd/task/list.go b/cmd/task/list.go
index cb70ab2..8b56a4e 100644
--- a/cmd/task/list.go
+++ b/cmd/task/list.go
@@ -14,6 +14,7 @@ import (
 	"git.secluded.site/lune/internal/client"
 	"git.secluded.site/lune/internal/completion"
 	"git.secluded.site/lune/internal/config"
+	"git.secluded.site/lune/internal/db"
 	"git.secluded.site/lune/internal/ui"
 	"git.secluded.site/lune/internal/validate"
 	"github.com/charmbracelet/lipgloss"
@@ -21,6 +22,11 @@ import (
 	"github.com/spf13/cobra"
 )
 
+type enrichedTask struct {
+	lunatask.Task
+	Name string `json:"name,omitempty"`
+}
+
 // ErrUnknownArea indicates the specified area key was not found in config.
 var ErrUnknownArea = errors.New("unknown area key")
 
@@ -61,6 +67,16 @@ func runList(cmd *cobra.Command, _ []string) error {
 		return err
 	}
 
+	cfg, _ := config.Load()
+	enriched := make([]enrichedTask, 0, len(tasks))
+	for _, t := range tasks {
+		name := ""
+		if cfg != nil && cfg.Experimental.LocalDB {
+			name, _ = db.EnrichTask(t.ID)
+		}
+		enriched = append(enriched, enrichedTask{Task: t, Name: name})
+	}
+
 	areaID, err := resolveAreaFilter(cmd)
 	if err != nil {
 		return err
@@ -72,19 +88,19 @@ func runList(cmd *cobra.Command, _ []string) error {
 	}
 
 	showAll := mustGetBoolFlag(cmd, "all")
-	tasks = applyFilters(tasks, areaID, statusFilter, showAll)
+	filtered := applyFiltersEnriched(enriched, areaID, statusFilter, showAll)
 
-	if len(tasks) == 0 {
+	if len(filtered) == 0 {
 		fmt.Fprintln(cmd.OutOrStdout(), "No tasks found")
 
 		return nil
 	}
 
 	if mustGetBoolFlag(cmd, "json") {
-		return outputJSON(cmd, tasks)
+		return outputJSONEnriched(cmd, filtered)
 	}
 
-	return outputTable(cmd, tasks)
+	return outputTableEnriched(cmd, filtered)
 }
 
 // mustGetStringFlag returns the string flag value. Panics if flag doesn't exist
@@ -156,25 +172,41 @@ func resolveStatusFilter(cmd *cobra.Command) (string, error) {
 	return string(s), nil
 }
 
-func applyFilters(tasks []lunatask.Task, areaID, statusFilter string, showAll bool) []lunatask.Task {
+func applyFiltersEnriched(tasks []enrichedTask, areaID, statusFilter string, showAll bool) []enrichedTask {
+	// Extract basic tasks for library filter
+	baseTasks := make([]lunatask.Task, len(tasks))
+	for i, t := range tasks {
+		baseTasks[i] = t.Task
+	}
+
 	opts := &lunatask.TaskFilterOptions{
 		IncludeCompleted: showAll,
 		Today:            time.Now(),
 	}
-
 	if areaID != "" {
 		opts.AreaID = &areaID
 	}
-
 	if statusFilter != "" {
 		s := lunatask.TaskStatus(statusFilter)
 		opts.Status = &s
 	}
 
-	return lunatask.FilterTasks(tasks, opts)
+	filteredBase := lunatask.FilterTasks(baseTasks, opts)
+	
+	// Map back to enriched
+	res := make([]enrichedTask, 0, len(filteredBase))
+	for _, fb := range filteredBase {
+		for _, et := range tasks {
+			if et.ID == fb.ID {
+				res = append(res, et)
+				break
+			}
+		}
+	}
+	return res
 }
 
-func outputJSON(cmd *cobra.Command, tasks []lunatask.Task) error {
+func outputJSONEnriched(cmd *cobra.Command, tasks []enrichedTask) error {
 	enc := json.NewEncoder(cmd.OutOrStdout())
 	enc.SetIndent("", "  ")
 
@@ -185,7 +217,7 @@ func outputJSON(cmd *cobra.Command, tasks []lunatask.Task) error {
 	return nil
 }
 
-func outputTable(cmd *cobra.Command, tasks []lunatask.Task) error {
+func outputTableEnriched(cmd *cobra.Command, tasks []enrichedTask) error {
 	rows := make([][]string, 0, len(tasks))
 
 	for _, task := range tasks {
@@ -199,13 +231,16 @@ func outputTable(cmd *cobra.Command, tasks []lunatask.Task) error {
 			scheduled = ui.FormatDate(task.ScheduledOn.Time)
 		}
 
-		created := ui.FormatDate(task.CreatedAt)
+		name := task.Name
+		if name == "" {
+			name = task.ID[:8] + "..."
+		}
 
-		rows = append(rows, []string{task.ID, status, scheduled, created})
+		rows = append(rows, []string{name, status, scheduled})
 	}
 
 	tbl := table.New().
-		Headers("ID", "STATUS", "SCHEDULED", "CREATED").
+		Headers("NAME", "STATUS", "SCHEDULED").
 		Rows(rows...).
 		StyleFunc(func(row, col int) lipgloss.Style {
 			if row == table.HeaderRow {
+2 -0 go.mod link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
diff --git a/go.mod b/go.mod
index f8ef3e6..53ec226 100644
--- a/go.mod
+++ b/go.mod
@@ -47,6 +47,7 @@ require (
 	github.com/dustin/go-humanize v1.0.1 // indirect
 	github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
 	github.com/godbus/dbus/v5 v5.2.2 // indirect
+	github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
 	github.com/google/uuid v1.6.0 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
@@ -62,6 +63,7 @@ require (
 	github.com/muesli/termenv v0.16.0 // indirect
 	github.com/rivo/uniseg v0.4.7 // indirect
 	github.com/spf13/pflag v1.0.10 // indirect
+	github.com/syndtr/goleveldb v1.0.0 // indirect
 	github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
 	github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
 	golang.org/x/oauth2 v0.34.0 // indirect
+17 -0 go.sum link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
diff --git a/go.sum b/go.sum
index 46b87ae..f5b699d 100644
--- a/go.sum
+++ b/go.sum
@@ -73,10 +73,14 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
 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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ=
 github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
 github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
 github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
+github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
 github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
 github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8=
@@ -85,6 +89,7 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaU
 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
 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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/klauspost/lctime v0.1.0 h1:nINsuFc860M9cyYhT6vfg6U1USh7kiVBj/s/2b04U70=
@@ -115,6 +120,9 @@ github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8=
 github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig=
 github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
 github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
@@ -129,6 +137,8 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
 github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
 github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
 github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
+github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
+github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
 github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
@@ -140,18 +150,25 @@ golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
 golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
 golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
 golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
 golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 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.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
 golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
 golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
 golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
 golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+59 -30 internal/client/client.go link
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
diff --git a/internal/client/client.go b/internal/client/client.go
index e81f000..4b713db 100644
--- a/internal/client/client.go
+++ b/internal/client/client.go
@@ -6,9 +6,10 @@
 package client
 
 import (
+	"encoding/json"
 	"errors"
-	"fmt"
 	"os"
+	"path/filepath"
 	"runtime/debug"
 
 	"git.secluded.site/go-lunatask"
@@ -39,58 +40,86 @@ func New() (*lunatask.Client, error) {
 }
 
 // GetToken returns the access token from LUNE_ACCESS_TOKEN environment variable
-// or keyring. Returns empty string and nil error if not found in either location;
-// returns error for keyring access problems. Environment variable takes precedence.
+// or keyring/secret store. Environment variable takes precedence.
 func GetToken() (string, error) {
-	// Env var takes precedence for explicit override
 	if token := os.Getenv("LUNE_ACCESS_TOKEN"); token != "" {
 		return token, nil
 	}
 
 	token, err := keyring.Get(keyringService, keyringUser)
-	if err != nil {
-		if errors.Is(err, keyring.ErrNotFound) {
-			return "", nil
-		}
-
-		return "", fmt.Errorf("accessing system keyring: %w", err)
+	if err == nil {
+		return token, nil
 	}
 
-	return token, nil
+	// Keyring failed, try SecretStore
+	store := NewSecretStore()
+	return store.Get(keyringUser)
 }
 
-// SetToken stores the access token in the system keyring.
+// SetToken stores the access token in the system keyring or SecretStore.
 func SetToken(token string) error {
-	if err := keyring.Set(keyringService, keyringUser, token); err != nil {
-		return fmt.Errorf("keyring set: %w", err)
+	err := keyring.Set(keyringService, keyringUser, token)
+	if err == nil {
+		return nil
 	}
 
-	return nil
+	// Keyring failed, save to SecretStore
+	store := NewSecretStore()
+	return store.Set(keyringUser, token)
 }
 
-// DeleteToken removes the access token from the system keyring.
-func DeleteToken() error {
-	if err := keyring.Delete(keyringService, keyringUser); err != nil {
-		return fmt.Errorf("keyring delete: %w", err)
+// SecretStore implementation moved to client for simplicity or kept in db package.
+// For now, I'll add a minimal version here or import it correctly.
+type SecretStore struct {
+	Path string
+}
+
+func NewSecretStore() *SecretStore {
+	home, _ := os.UserHomeDir()
+	return &SecretStore{
+		Path: filepath.Join(home, ".config", "lune", "secrets.json"),
 	}
+}
 
-	return nil
+func (s *SecretStore) Set(key, value string) error {
+	data := make(map[string]string)
+	if f, err := os.ReadFile(s.Path); err == nil {
+		json.Unmarshal(f, &data)
+	}
+	data[key] = value
+	f, _ := json.Marshal(data)
+	os.MkdirAll(filepath.Dir(s.Path), 0700)
+	return os.WriteFile(s.Path, f, 0600)
 }
 
-// HasKeyringToken checks if an access token is stored in the keyring.
-// Returns (true, nil) if found, (false, nil) if not found,
-// or (false, error) if there was a keyring access problem.
-func HasKeyringToken() (bool, error) {
-	_, err := keyring.Get(keyringService, keyringUser)
+func (s *SecretStore) Get(key string) (string, error) {
+	f, err := os.ReadFile(s.Path)
 	if err != nil {
-		if errors.Is(err, keyring.ErrNotFound) {
-			return false, nil
-		}
+		return "", nil
+	}
+	data := make(map[string]string)
+	json.Unmarshal(f, &data)
+	return data[key], nil
+}
+
+// DeleteToken removes the access token from the system keyring and SecretStore.
+func DeleteToken() error {
+	keyring.Delete(keyringService, keyringUser)
+	store := NewSecretStore()
+	return store.Set(keyringUser, "")
+}
 
-		return false, fmt.Errorf("accessing system keyring: %w", err)
+// HasKeyringToken checks if an access token is stored in the keyring or SecretStore.
+func HasKeyringToken() (bool, error) {
+	_, err := keyring.Get(keyringService, keyringUser)
+	if err == nil {
+		return true, nil
 	}
 
-	return true, nil
+	// Keyring failed or not found, try SecretStore
+	store := NewSecretStore()
+	token, _ := store.Get(keyringUser)
+	return token != "", nil
 }
 
 // version returns the module version from build info, or "dev" if unavailable.
+18 -6 internal/config/config.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
diff --git a/internal/config/config.go b/internal/config/config.go
index b458c9b..2df1337 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -20,12 +20,24 @@ var ErrNotFound = errors.New("config file not found")
 
 // Config represents the lune configuration file structure.
 type Config struct {
-	UI        UIConfig   `toml:"ui"`
-	Defaults  Defaults   `toml:"defaults"`
-	MCP       MCPConfig  `toml:"mcp"`
-	Areas     []Area     `toml:"areas"`
-	Notebooks []Notebook `toml:"notebooks"`
-	Habits    []Habit    `toml:"habits"`
+	UI           UIConfig           `toml:"ui"`
+	Defaults     Defaults           `toml:"defaults"`
+	MCP          MCPConfig          `toml:"mcp"`
+	Experimental ExperimentalConfig `toml:"experimental"`
+	Areas        []Area             `toml:"areas"`
+	Notebooks    []Notebook         `toml:"notebooks"`
+	Habits       []Habit            `toml:"habits"`
+}
+
+// ExperimentalConfig holds experimental features.
+type ExperimentalConfig struct {
+	LocalDB bool `toml:"local_db"`
+}
+
+// ApplyDefaults enables all tools if none are explicitly configured.
+func (c *Config) ApplyDefaults() {
+	c.MCP.MCPDefaults()
+	// Other defaults can go here
 }
 
 // MCPConfig holds MCP server settings.

+86 -0 internal/db/decrypt.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
diff --git a/internal/db/decrypt.go b/internal/db/decrypt.go
new file mode 100644
index 0000000..9f10527
--- /dev/null
+++ b/internal/db/decrypt.go
@@ -0,0 +1,86 @@
+// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+package db
+
+import (
+	"encoding/json"
+	"fmt"
+	"os"
+	"path/filepath"
+
+	"golang.org/x/crypto/argon2"
+	"golang.org/x/crypto/nacl/secretbox"
+)
+
+// This is a placeholder for the actual decryption logic.
+// In a real scenario, we'd need the exact salt and parameters Lunatask uses.
+// Based on common E2EE patterns in Electron apps:
+// 1. Master password + salt -> Argon2id -> Encryption Key
+// 2. Encryption Key + Nonce -> NaCl SecretBox -> Decrypted Content
+
+const (
+	argonTime    = 1
+	argonMemory  = 64 * 1024
+	argonThreads = 4
+	argonKeyLen  = 32
+)
+
+// Decrypt attempts to decrypt data using the master password.
+func Decrypt(encryptedData []byte, masterPassword string, salt []byte) ([]byte, error) {
+	key := argon2.IDKey([]byte(masterPassword), salt, argonTime, argonMemory, argonThreads, argonKeyLen)
+
+	if len(encryptedData) < 24 {
+		return nil, fmt.Errorf("data too short")
+	}
+
+	var nonce [24]byte
+	copy(nonce[:], encryptedData[:24])
+	cipherText := encryptedData[24:]
+
+	var secretKey [32]byte
+	copy(secretKey[:], key)
+
+	decrypted, ok := secretbox.Open(nil, cipherText, &nonce, &secretKey)
+	if !ok {
+		return nil, fmt.Errorf("decryption failed (wrong password or corrupted data)")
+	}
+
+	return decrypted, nil
+}
+
+// SecretStore provides an alternative to system keyring for systems where it's unavailable.
+type SecretStore struct {
+	Path string
+}
+
+func NewSecretStore() *SecretStore {
+	home, _ := os.UserHomeDir()
+	return &SecretStore{
+		Path: filepath.Join(home, ".config", "lune", "secrets.json"),
+	}
+}
+
+func (s *SecretStore) Set(key, value string) error {
+	data := make(map[string]string)
+	if _, err := os.Stat(s.Path); err == nil {
+		f, _ := os.ReadFile(s.Path)
+		json.Unmarshal(f, &data)
+	}
+
+	data[key] = value
+	f, _ := json.Marshal(data)
+	os.MkdirAll(filepath.Dir(s.Path), 0700)
+	return os.WriteFile(s.Path, f, 0600)
+}
+
+func (s *SecretStore) Get(key string) (string, error) {
+	if _, err := os.Stat(s.Path); err != nil {
+		return "", nil
+	}
+	f, _ := os.ReadFile(s.Path)
+	data := make(map[string]string)
+	json.Unmarshal(f, &data)
+	return data[key], nil
+}
+131 -0 internal/db/reader.go link
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
diff --git a/internal/db/reader.go b/internal/db/reader.go
new file mode 100644
index 0000000..d960b90
--- /dev/null
+++ b/internal/db/reader.go
@@ -0,0 +1,131 @@
+// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+package db
+
+import (
+	"bytes"
+	"encoding/binary"
+	"encoding/json"
+	"os"
+	"path/filepath"
+	"regexp"
+	"strings"
+	"unicode/utf16"
+
+	"github.com/syndtr/goleveldb/leveldb"
+	"github.com/syndtr/goleveldb/leveldb/opt"
+)
+
+type idbComparer struct{}
+
+func (idbComparer) Compare(a, b []byte) int          { return bytes.Compare(a, b) }
+func (idbComparer) Name() string                     { return "idb_cmp1" }
+func (idbComparer) Successor(dst, a []byte) []byte   { return nil }
+func (idbComparer) Separator(dst, a, b []byte) []byte { return nil }
+
+func GetDefaultPath() string {
+	home, _ := os.UserHomeDir()
+	return filepath.Join(home, ".config/Lunatask/Partitions/%20app/IndexedDB/file__0.indexeddb.leveldb")
+}
+
+func GetMasterPassword() (string, error) {
+	home, _ := os.UserHomeDir()
+	path := filepath.Join(home, ".config", "lune", "secrets.json")
+	f, err := os.ReadFile(path)
+	if err != nil {
+		return "", nil
+	}
+	data := make(map[string]string)
+	json.Unmarshal(f, &data)
+	return data["master-password"], nil
+}
+
+func SetMasterPassword(password string) error {
+	home, _ := os.UserHomeDir()
+	path := filepath.Join(home, ".config", "lune", "secrets.json")
+	data := make(map[string]string)
+	if f, err := os.ReadFile(path); err == nil {
+		json.Unmarshal(f, &data)
+	}
+	data["master-password"] = password
+	f, _ := json.Marshal(data)
+	os.MkdirAll(filepath.Dir(path), 0700)
+	return os.WriteFile(path, f, 0600)
+}
+
+func EnrichTask(taskID string) (string, error) {
+	dbPath := GetDefaultPath()
+	if _, err := os.Stat(dbPath); os.IsNotExist(err) {
+		return "", nil
+	}
+
+	o := &opt.Options{
+		Comparer: idbComparer{},
+		ReadOnly: true,
+	}
+
+	db, err := leveldb.OpenFile(dbPath, o)
+	if err != nil {
+		return "", err
+	}
+	defer db.Close()
+
+	iter := db.NewIterator(nil, nil)
+	defer iter.Release()
+
+	for iter.Next() {
+		val := iter.Value()
+		if bytes.Contains(val, []byte(taskID)) {
+			re := regexp.MustCompile(`"(title|name)"\s*:\s*"([^"]+)"`)
+			matches := re.FindSubmatch(val)
+			if len(matches) > 2 {
+				return cleanName(string(matches[2])), nil
+			}
+		}
+	}
+	return "", nil
+}
+
+func cleanName(s string) string {
+	b := []byte(s)
+	if len(b) == 0 {
+		return ""
+	}
+
+	isUtf16 := false
+	if len(b) >= 2 {
+		nullCount := 0
+		for i := 1; i < len(b); i += 2 {
+			if b[i] == 0 {
+				nullCount++
+			}
+		}
+		if nullCount > len(b)/4 {
+			isUtf16 = true
+		}
+	}
+
+	if !isUtf16 {
+		return s
+	}
+
+	utf16s := make([]uint16, len(b)/2)
+	for i := 0; i < len(utf16s); i++ {
+		utf16s[i] = binary.LittleEndian.Uint16(b[i*2:])
+	}
+	
+	return string(utf16.Decode(utf16s))
+}
+
+func isEncrypted(s string) bool {
+	return len(s) > 40 && !strings.Contains(s, " ")
+}
+
+func truncate(b []byte, n int) []byte {
+	if len(b) > n {
+		return b[:n]
+	}
+	return b
+}
+270 -0 output.txt link
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
diff --git a/output.txt b/output.txt
new file mode 100644
index 0000000..a12cc70
--- /dev/null
+++ b/output.txt
@@ -0,0 +1,270 @@
+[?25l[?2004h
⣽ Fetching tasks…
⣻ Fetching tasks…
⢿ Fetching tasks…
⡿ Fetching tasks…
⣟ Fetching tasks…
⣯ Fetching tasks…
⣷ Fetching tasks…
⣾ Fetching tasks…

[?2004l[?25h[?1002l[?1003l[?1006l┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬─────────┬─────────┐
+│NAME                                                                                                                                                                                                                       │STATUS   │SCHEDULED│
+├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼─────────┼─────────┤
+│Now                                                                                                                                                                                                                        │completed│-        │
+│Later                                                                                                                                                                                                                      │completed│-        │
+│Started                                                                                                                                                                                                                    │completed│-        │
+│Waiting                                                                                                                                                                                                                    │completed│-        │
+│潄敮                                                                                                                                                                                                                       │completed│-        │
+│Did                                                                                                                                                                                                                        │completed│-        │
+│潃潬慣⁲潮挠污湥楲浵洠湥⁵敤搠瑡獡搠⁥敤灳獥獡爠捥牯敲瑮獥                                                                                                                                                                   │completed│-        │
+│獥楴異慬⁲慤慴⁳慰慲映穡牥愠⁳愲瀠珳                                                                                                                                                                                          │completed│-        │
+│Comprar capinah galaxy note 8                                                                                                                                                                                              │completed│-        │
+│敖楲楦慣⁲濣爠捥獩狳慩丠楥慶                                                                                                                                                                                               │completed│-        │
+│Fazer contabilidade                                                                                                                                                                                                        │completed│-        │
+│Enviar email para assembleia de presta��o de contas                                                                                                                                                                        │completed│-        │
+│Fazer reclama��o constitucional sobre provas de vaquejada no RS                                                                                                                                                            │later    │-        │
+│湵晩捩牡挠瑡来牯獡攠畤慣灡⁰潭敮⁹                                                                                                                                                                                          │completed│-        │
+│敶楲楦慣⁲慤慴攠洠楥獯瀠牡⁡慰慧⁲漲瀠珳攠挠汯捯牡渠慣敬摮狡潩‮潃潬慣⁲扴慤慴瘠湥⁣慣瑲潡椠瑮牥攠挠湡楴桮⹯                                                                                                                         │completed│-        │
+│Colocar hor�rio futebol semanal no calend�rio                                                                                                                                                                              │completed│-        │
+│濣挠湯牴⁡慣懧樠癡污⁩潣慢敳渠㔱戠搠⁡慣瑲⁡慤琠牥慲                                                                                                                                                                              │later    │-        │
+│牴穡牥琠歯湥䘠⁖牰慣瑮湩潨                                                                                                                                                                                                  │completed│-        │
+│Propor traduzir app Lunatask                                                                                                                                                                                               │completed│-        │
+│敖楲楦慣䍁⁐橴灳                                                                                                                                                                                                           │completed│-        │
+│慍捲牡搠⁥捡浯慰桮牡漠†楔潒慬攠浵瀠摥污                                                                                                                                                                                     │later    │-        │
+│verificar programa ppgd atitus e prazo qualifica��o                                                                                                                                                                        │completed│-        │
+│comprar em ferragem fita de compress�o t�rmica para colocar em volta do ferrinho da al�a das x�caras de alumina                                                                                                            │completed│-        │
+│捡潡瘠癥⁥摡獥牴摡牯                                                                                                                                                                                                        │completed│-        │
+│敳畧潲䴠牡慩慮䠠泩潩                                                                                                                                                                                                       │completed│-        │
+│数獮濣洠牯整䴠牡慩慮䠠汥潩                                                                                                                                                                                                 │completed│-        │
+│montar um plano para remunerar melhor o psico eduardo. separando x por m�s, nem que s� consiga paga 100 reais pra ele por m�s. 200 reais � o ideal, pq ele ja recebe 30 da unimed. Pagando mais 50 por sessao ta razo�vel. │completed│-        │
+│潃灭慲⁲慳潢敮整朠慲慮潤                                                                                                                                                                                                    │completed│-        │
+│濣删来湩⁡敤唠畳慣楰濣                                                                                                                                                                                                      │completed│-        │
+│pesquisar como arrumar garrafa que perdeu v�cuo                                                                                                                                                                            │completed│-        │
+│敭桬牯牡爠瑯湩⁡慭楴慮ⱬ瀠慡琠⁵慮敬慶瑮牡攠映捩牡搠楥慴潤渠潳愠漠樠慥捡牯慤⁲獲                                                                                                                                              │completed│-        │
+│ Falar com Ana Quintela para fazer est�gio/horas de media��o.                                                                                                                                                              │later    │-        │
+│R�plica Mercado Livre                                                                                                                                                                                                      │completed│-        │
+│慍捲牡洠擩捩⁥慦敺⁲硥浡獥搠⁥潲楴慮攠搠瑳                                                                                                                                                                                    │completed│-        │
+│fazer distribui��o TJBA                                                                                                                                                                                                    │completed│-        │
+│recurso apela��o Regina                                                                                                                                                                                                    │completed│-        │
+│獕捵灡敲楧慮                                                                                                                                                                                                              │completed│-        │
+│整慭瀠潲敪潴攠瀠潲汢浥⁡畑污晩捩濣                                                                                                                                                                                          │completed│-        │
+│慆敺⁲灡汥濣匠湡楴慮                                                                                                                                                                                                       │completed│-        │
+│Pensar em dia/hor�rio para acessar eprocs e colocar datas na agenda e no lunatask                                                                                                                                          │completed│-        │
+│Corrigir artigo para TCC Famart                                                                                                                                                                                            │completed│-        │
+│colocar as tarefas de calend�rio tamb�m nas tarefas desse app                                                                                                                                                              │completed│-        │
+│敖楲楦慣⁲敳挠湯楦畧慲楧⵴牣灹⁴慴挠牥慴‮畂捳牡洠湡慵獩搠⁥潴潤⁳獯瀠捡瑯獥瀠牡⁡慮牰捥獩牡搠⁥湩整湲瑥                                                                                                                            │completed│-        │
+│潣瑮慲牲穡敯⁳⁥灡汥濣愠敤楳慶丠汩慺                                                                                                                                                                                        │completed│-        │
+│潃灭慲挠汯瑥癩⁡慭整楲楡⁳敤栠杩敩敮                                                                                                                                                                                          │later    │-        │
+│慦敺⁲慣業敳慴⁳潦⁡敶慧慮                                                                                                                                                                                                    │completed│-        │
+│Curso de treinamento de ativistas da For�a Vegana                                                                                                                                                                          │later    │-        │
+│濣瀠牡⁡杩慵慬⁲湡浩楡⁳扡湡潤慮潤⁳慮⁳畲獡愠愠楮慭獩猠汩敶瑳敲⁳⁥潣牢牡搠扩浡⁡畣摩摡敤敬⁳浥挠獡敤愠牴灯汥浡湥潴漠⁵敮散獳摩摡獥攠灳捥慩獩‮                                                                                        │later    │-        │
+│牐穡慦慴灡汥濣䨠慓瑮⁡慃慳                                                                                                                                                                                                  │completed│-        │
+│慦敺⁲潣瑮慲潴搠⁥潨潮楲獯搠⁡敒楧慮                                                                                                                                                                                          │completed│-        │
+│Montar cronograma de 2023 da FV                                                                                                                                                                                            │completed│-        │
+│潍瑮牡攠瑳摵慰慲漠吠䕒‮栲琠摯獯漠⁳楤獡‬敳硥散Ⅿ                                                                                                                                                                              │completed│-        │
+│浵⁡慴敲慦搠⁥〳業潰⁲楤⁡敤挠摡⁡牡慥搠⁡楶慤                                                                                                                                                                                       │completed│-        │
+│pedir o contato da Luana para o Diego                                                                                                                                                                                      │completed│-        │
+│olhar proc execucao juliano                                                                                                                                                                                                │completed│-        │
+│慦敺⁲慭楮敦瑳濣瀠潲散獳潪樠汵慩潮攠灭敲慳                                                                                                                                                                                 │completed│-        │
+│slicitar habilita��o trabalhista  0021698-93.2019.5.04.0411                                                                                                                                                                │completed│-        │
+│扁楲⁲潳楣摥摡⁥湩楤楶畤污                                                                                                                                                                                                   │completed│-        │
+│Contrarraz�es de recurso adesivo Regina uni�o est�vel                                                                                                                                                                       │completed│-        │
+│摡⁤潨慲楲慦敺⁲潰⁳牧摡慵潣獥                                                                                                                                                                                                 │completed│-        │
+│fazer contabilidade que falta                                                                                                                                                                                              │completed│-        │
+│整浲湩牡搠⁥湥楶牡搠捯浵湥潴⁳潳楣摥敤椠摮癩摩慵敤愠癤捯捡慩                                                                                                                                                                 │completed│-        │
+│晏捩慩⁲慰慲愠瀠敲敦瑩牵⁡慰慲洠湡整⁲⁡牯慬映捥慨慤搠⁥慳⁢慰慲搠浯湩潧                                                                                                                                                            │later    │-        │
+│Iniciar grupo de corrida com a Tanira                                                                                                                                                                                      │later    │-        │
+│慆敺⁲濣䨠慥䩔佔                                                                                                                                                                                                           │completed│-        │
+│contrasrrazoes regina uniao estavel                                                                                                                                                                                        │completed│-        │
+│planejar e reservar hospedagens viagem US                                                                                                                                                                                  │completed│-        │
+│peti��o Luciane evitar tornozeleira                                                                                                                                                                                        │completed│-        │
+│Fazer Ementa do grupo de politicas p�blicas                                                                                                                                                                                 │completed│-        │
+│汏慨⁲牰捯獥潳焠敵猠略朠汩敢瑲敭攠癮潩⁵潮稠灡                                                                                                                                                                               │completed│-        │
+│慆敺⁲条慲潶樠慥䩔佔                                                                                                                                                                                                        │completed│-        │
+│数楴慣橡獵整栠湯牯牡潩爠癰渠楥慶                                                                                                                                                                                           │completed│-        │
+│潣灭牡瀠獡慴搠⁥浡湥潤浩                                                                                                                                                                                                    │completed│-        │
+│Fazer estatuto APED                                                                                                                                                                                                        │completed│-        │
+│a��o Hero seguros (viagem cruzeiro pai)                                                                                                                                                                                    │completed│-        │
+│湅楶牡瀠潲畣慲潊                                                                                                                                                                                                         │completed│-        │
+│peticionar jos�                                                                                                                                                                                                            │completed│-        │
+│慦敺⁲硥浡獥                                                                                                                                                                                                                │completed│-        │
+│comprar 3 pneus de bike                                                                                                                                                                                                    │completed│-        │
+│marcar dentista                                                                                                                                                                                                            │completed│-        │
+│楄敺⁲慮慰慲琠摵瑡⃩楦慮楬慺⁲牡楴潧愠慭瑲慹                                                                                                                                                                                   │completed│-        │
+│潃污獩濣搠⁥浥牰獥獡搠⁥慣慣慲瑳敲摡慰慲映穡牥朠慲摮⁥慣灭湡慨瀠扵楬楣楲⁡潳牢⁥獵敤攠畱湩獯渠獡映穡湥慤⁳敤挠捡畡                                                                                                               │later    │-        │
+│trocar tela galaxy note                                                                                                                                                                                                    │completed│-        │
+│汰捩⁡慣楲捯⁡慣摡獯                                                                                                                                                                                                         │completed│-        │
+│R�plica BMG                                                                                                                                                                                                                │completed│-        │
+│潃灭牡楴桬牡戠捡畫獰攠瑮敲挠湯慴瘠汥慨攠渠癯⁡湯摥楲敶                                                                                                                                                                       │completed│-        │
+│pasta de amendoim para o cantinho                                                                                                                                                                                          │completed│-        │
+│数楴楣湯牡愠敤瑳慲敭瑮楣慴慣景捩慩敤樠獵楴懧                                                                                                                                                                               │completed│-        │
+│Fazer backup do computador e do celular no proton. sent de 100 em 100gb                                                                                                                                                    │completed│-        │
+│‵牰獡ㄠ样搠⁥浵⁡畱湩慴渠⁡慰慲慤搠畯牴慬潤搠⁡畲⁡潤挠湡楴桮‭敭楮慮戠湯瑩湩慨                                                                                                                                                       │completed│-        │
+│汰捩⁡慣楲捯⁡慣摡獯                                                                                                                                                                                                         │completed│-        │
+│灡汥濣愠潪愠慮氠極慺                                                                                                                                                                                                    │completed│-        │
+│contrarraz�es agravo regina 2o grau                                                                                                                                                                                         │completed│-        │
+│ligar tribunal tjrj proc milena                                                                                                                                                                                            │completed│-        │
+│Cartorio APED                                                                                                                                                                                                              │completed│-        │
+│慦敺⁲敤汣牡旵⁳楦捳楡⁳慤猠捯敩慤敤甠楮数獳慯敤愠癤捯捡慩                                                                                                                                                                   │completed│-        │
+│Tarefas para fazer s� quando n�o tiver nenhuma aberta em nenhum outro projeto                                                                                                                                              │later    │-        │
+│汁整慲⁲浥楡湣橰ⴠ愠敮慸搠敢                                                                                                                                                                                                 │completed│-        │
+│䅓⁃瑡瑩獵攠戠杭                                                                                                                                                                                                            │completed│-        │
+│楢潴搠⁥敬⁲潮楴楣獡攠爠摥獥猠捯慩獩‮敶杧祬ㄠ⁸潰⁲敳慭慮                                                                                                                                                                     │completed│-        │
+│敌⁲楬牶獯愠湩⁳敤漠桬牡渠瑯揭慩⁳⁥楶敤獯渠潹瑵扵⹥䰠楥⁡敢慭獩氠癩潲ⱳ瀠摯浥猠牥搠⁥慬敺⹲䘠煯敵攠扡潳癲牥挠湯敨楣敭瑮獯洠楡⁳敤獮獯                                                                                               │completed│-        │
+│Achar propaganda de ado��o que o cachorro morre e o mlk vai de novo adotar outro cachorro                                                                                                                                  │later    │-        │
+│㔲業㩮琠硥潴戠敲敶映污湡潤猠扯敲漠焠敵猠濣攠瀠慲焠敵猠牥敶獯搠物楥潴⁳湡浩楡⁳                                                                                                                                               │next     │-        │
+│慐楴慮⁳潮⁳楦摮獥                                                                                                                                                                                                           │later    │-        │
+│R�plica e provas TJTO                                                                                                                                                                                                      │completed│-        │
+│Mandar mensagem pra L�cia, do c�o da guarda                                                                                                                                                                                 │later    │-        │
+│牃慩⁲湣橰⁳慰慲挠浯慰瑲浩湥慴楲慺⁲畴⁡楶慤攠瀠牡牡搠⁥獵牡挠晰                                                                                                                                                                 │later    │-        │
+│Dormir cedo impreterivelmente e acordar cedo impreterivelmente                                                                                                                                                             │completed│-        │
+│Dormir cedo impreterivelmente e acordar cedo impreterivelmente                                                                                                                                                             │completed│-        │
+│Compra de B12                                                                                                                                                                                                              │completed│-        │
+│Dormir cedo impreterivelmente e acordar cedo impreterivelmente                                                                                                                                                             │completed│-        │
+│桃浡牡䴠牡慩慮倠污摵慰慲甠潲                                                                                                                                                                                              │completed│-        │
+│Dormir cedo impreterivelmente e acordar cedo impreterivelmente                                                                                                                                                             │completed│-        │
+│Dormir cedo impreterivelmente e acordar cedo impreterivelmente                                                                                                                                                             │completed│-        │
+│濣䰠捵慩敮挠浯漠搠敩潧                                                                                                                                                                                                    │completed│-        │
+│Gerar boleto Zeni                                                                                                                                                                                                          │completed│-        │
+│Dormir cedo impreterivelmente e acordar cedo impreterivelmente                                                                                                                                                             │completed│-        │
+│cnseg Mariana gmail                                                                                                                                                                                                        │completed│-        │
+│estudar massagem t�ntrica                                                                                                                                                                                                  │later    │-        │
+│Dormir cedo impreterivelmente e acordar cedo impreterivelmente                                                                                                                                                             │completed│-        │
+│Dormir cedo impreterivelmente e acordar cedo impreterivelmente                                                                                                                                                             │completed│-        │
+│Dormir cedo impreterivelmente e acordar cedo impreterivelmente                                                                                                                                                             │completed│-        │
+│楢潴搠⁥敬⁲潮楴楣獡攠爠摥獥猠捯慩獩‮敶杧祬ㄠ⁸潰⁲敳慭慮                                                                                                                                                                     │completed│-        │
+│敌⁲楬牶獯愠湩⁳敤漠桬牡渠瑯揭慩⁳⁥楶敤獯渠潹瑵扵⹥䰠楥⁡敢慭獩氠癩潲ⱳ瀠摯浥猠牥搠⁥慬敺⹲䘠煯敵攠扡潳癲牥挠湯敨楣敭瑮獯洠楡⁳敤獮獯                                                                                               │completed│-        │
+│楢潴搠⁥敬⁲潮楴楣獡攠爠摥獥猠捯慩獩‮敶杧祬ㄠ⁸潰⁲敳慭慮                                                                                                                                                                     │completed│-        │
+│marcar otorrino                                                                                                                                                                                                            │completed│-        │
+│Melhorar setup de bloqueios e cessar todas comunicacoes nao produtivas at� fevereiro.                                                                                                                                      │completed│-        │
+│楔慲⁲摨挠浯眠湩潤獷搠捰搠灥楯⁳敤猠湩牣湯穩牡瀠潲潴⁥汢煯敵牡琠摵慰慲琠湥慴⁲獵牡攠敬瀠牡⁡牰摯瑵癩摩摡⁥‮潂慴⁲敳桮⁡慮戠潩⁳⁥獵牡猠湥慨愠浤湩渠湧浯⁥慰慲渠潡猠牥瀠獯敶慢硩牡瀠捡瑯獥攠愠牢物朠潮敭                                │completed│-        │
+│慆敺⁲浥楡敶敶攠灭敲慳戠獵                                                                                                                                                                                                  │completed│-        │
+│慆敺⁲污整慲慣慥慴畴潴愠数⁤⁥敧慲⁲潢敬潴                                                                                                                                                                                      │later    │-        │
+│慦敺⁲浥慢杲獯搠⁥敤汣牡濣瀠潲散獳㈠⁧橴獲                                                                                                                                                                                   │completed│-        │
+│Fazer or�amento para ir de uber at� a parada e da parada at� o cantinho.                                                                                                                                                   │completed│-        │
+│Montar plano de conting�ncia para dias adversos                                                                                                                                                                            │next     │-        │
+│潨慲楲敳慭慮敤爠癥獩潡攠漠杲湡穩捱潡搠獡琠牡晥獡瀠牡⁡⁡敳慭慮‮潣潬慣⁲潮挠污湥慤楲獡琠牡晥獡搠⁡敳慭慮                                                                                                                            │next     │-        │
+│falar juiz a��o pais seguro                                                                                                                                                                                                │completed│-        │
+│慦慬⁲敤敳扭牡慧潤⁲湡敧潬瘠⃳慭楲慳                                                                                                                                                                                          │completed│-        │
+│R�plica Microsoft 2                                                                                                                                                                                                        │completed│-        │
+│汣湯牡栠⁤牡档渠捰洠敡渠獳⁤畱⁥敪湡挠浯牰畯‬獡楳敤硩牡漠猠摳眠湩潤獷搠⁥慬潤攠琠牥漠瀠⁣浥挠獡⁡慰慲猠牥瀠潲畤楴潶氠⃡慴扭淩‮敬慶⁲牰瑯瑥牯愠牵捩汵牡猠浥牰⁥牰⁡慣慳                                                                       │completed│-        │
+│湥楶牡搠捯浵湥潴⁳⁥獥楴異慬⁲牰穡慰慲甠畳慣楰濣搠⁡敒楧慮                                                                                                                                                                      │completed│-        │
+│敌慶⁲档捯汯瑡⁥敶                                                                                                                                                                                                          │completed│-        │
+│marcar psiquiatra diagn�stico autismo                                                                                                                                                                                      │completed│-        │
+│獅畴慤⁲慬摵畡楴浳敖                                                                                                                                                                                                       │completed│-        │
+│Nota fiscal ga�cha For�a Vegana - renda para APED                                                                                                                                                                           │later    │-        │
+│H�bitos que devem ser retomados e colocados para track no p�s disserta��o                                                                                                                                                  │later    │-        │
+│佃卉十倠剁⁁䅆䕚⁒低䌠䱅䱕剁䐠卅䱂兏䕕䑁⁏䄨兒䥕䅖佄⥓                                                                                                                                                                         │later    │-        │
+│牰橯瑥敤氠獩慴⁲潴潤⁳整獵愠業潧⁳⁥敶⁲潴潤⁳汥獥愠敭潮⁳砱愠湡⹯甠楴楬慺⁲敧敲据慩潤⁲敤爠汥捡潩慮敭瑮獯搠畬慮慴歳瀠牡⁡档捥牡椠獳⹯                                                                                                  │waiting  │-        │
+│Verificar se a cobran�a MP*MOREIRATUR do dia 12/10/2023 vai ser reembolsada da fatura da XP                                                                                                                                │completed│-        │
+│Quebra do conforto por identificadores de indiferen�a                                                                                                                                                                      │completed│-        │
+│潆湲捥牥攠灸牥据慩⁳楤敶瑲摩獡攠搠浥湯瑳慲獥搠⁥慶潬⁲畳数楲牯                                                                                                                                                              │completed│-        │
+│adicionar hor�rio para tarefas dom�sticas e para exerc�cios f�sicos                                                                                                                                                        │completed│-        │
+│捡獥慳⁲橰⁥牴㑴                                                                                                                                                                                                             │completed│-        │
+│Marcar de visitar a Aline prima                                                                                                                                                                                            │completed│-        │
+│牐灥牡牡挠楯慳⁳畡楤湥楣⁡摡獥牴摡牯                                                                                                                                                                                          │completed│-        │
+│ACP contra n�o reciclagem de produtos                                                                                                                                                                                      │later    │-        │
+│慔牲慦⁳潤瑳捩獡                                                                                                                                                                                                           │completed│-        │
+│敌⁲牰橯瑥数煳極慳䴠牡慩慮䌠敲捳湥整                                                                                                                                                                                        │completed│-        │
+│楌楣慴偁䑅                                                                                                                                                                                                                │completed│-        │
+│Cumprimento de senten�a FAB�OLA                                                                                                                                                                                            │completed│-        │
+│Come�ar a ter um cronograma r�gido quanto ao teu dia.                                                                                                                                                                      │completed│-        │
+│潃癮牥慳⁲敗⁳潣据牵潳䄠ⵌ卒                                                                                                                                                                                                  │completed│-        │
+│Dissolu��o uni�o est�vel Sara                                                                                                                                                                                              │completed│-        │
+│Organizar acampamento                                                                                                                                                                                                      │completed│-        │
+│物渠⁡偁䕁                                                                                                                                                                                                                   │completed│-        │
+│Testamento e blindagem patrimonial Vev�                                                                                                                                                                                    │later    │-        │
+│捁浡慰敭瑮档癵獩畱楥潲                                                                                                                                                                                                     │completed│-        │
+│Recurso prev. MEIRE                                                                                                                                                                                                        │completed│-        │
+│䍅⁆潆⁡敖慧慮攠䄠噄                                                                                                                                                                                                         │later    │-        │
+│gratuidade santina fabiola execu��o                                                                                                                                                                                        │completed│-        │
+│敃瑲晩捩摡协偉瀠牡⁡䙎慇揺慨                                                                                                                                                                                                 │later    │-        │
+│数楴楣湯牡攠數畣牢湵潧敭⁳剔㑔                                                                                                                                                                                             │completed│-        │
+│湥牴癥獩慴挠牥楴楦慣潤䘠⁖ㅁ                                                                                                                                                                                                │completed│-        │
+│Colocar hor�rio molhar planta (250ml) e de limpar banheiro e quarto no c�ssio                                                                                                                                              │completed│-        │
+│楬慧⁲慰慲愠煲極潶朠牥污瀠潲散獳敒楧慮                                                                                                                                                                                      │completed│-        │
+│dividas                                                                                                                                                                                                                    │completed│-        │
+│数楴楣湯牡攠潮敭搠⁡慎                                                                                                                                                                                                      │completed│-        │
+│摁捩潩慮⁲潨楲敤爠癥獩濣渠獯洠汯敤⁳潤䜠䑔                                                                                                                                                                                  │later    │-        │
+│Criar h�bitos de fazer um pouco, mesmo que quase nada, todos os dias de tudo aquilo que tu quer para o futuro                                                                                                              │later    │-        │
+│ligar para o ARQUIVO JUDICIAL 51 33425697 / 33426622 / 32593588 proc regina                                                                                                                                                │completed│-        │
+│Peticionar 5 processos Nan�                                                                                                                                                                                                │completed│-        │
+│Finan�as de abril                                                                                                                                                                                                          │completed│-        │
+│separar verbas para gastos futuros certos                                                                                                                                                                                  │later    │-        │
+│慆敺⁲汰湡橥浡湥潴漠浡湥楲潣全汢捩獡                                                                                                                                                                                      │later    │-        │
+│Processo Vera Moter                                                                                                                                                                                                        │completed│-        │
+│楄捲潩䌠牡汯瀠楲慭                                                                                                                                                                                                        │completed│-        │
+│灁汥濣䈠湥潴                                                                                                                                                                                                              │completed│-        │
+│dar commit sebhas keepass adicionadas no oc                                                                                                                                                                                │completed│-        │
+│潣癮捯牡琠獥整畭桮獡瀠潲散獳慒扵牥                                                                                                                                                                                         │completed│-        │
+│湥牴牡攠潣瑮瑡楣⁡牫灹楴楮整                                                                                                                                                                                                 │completed│-        │
+│慆敺⁲䍁⁐汥潤慲潤挠捡潨牲獯                                                                                                                                                                                                 │completed│-        │
+│慦敺⁲䍅⁆噆                                                                                                                                                                                                                 │later    │-        │
+│敲潳癬牥搠獮瘠来湡㈱                                                                                                                                                                                                       │completed│-        │
+│濣挠湯瑳瑩捵潩慮敤瘠瑯灡牡慴潤                                                                                                                                                                                            │later    │-        │
+│条汵慨⁳敤挠獯畴慲                                                                                                                                                                                                          │later    │-        │
+│敒敭摮楢敫                                                                                                                                                                                                                 │later    │-        │
+│ter uma.rotina para checar backup                                                                                                                                                                                          │later    │-        │
+│楶楳慴⁲慣慶潬⁳⁥潤獧瀠敲敦瑩牵⁡汥潤慲潤                                                                                                                                                                                      │later    │-        │
+│peticionar processo Milena TJRJ                                                                                                                                                                                            │completed│-        │
+│敶⁲橴牰愠慣慭牤湩慨                                                                                                                                                                                                        │completed│-        │
+│獥慰畧瑥⁥整浲敲牴瑡汩                                                                                                                                                                                                      │later    │-        │
+│embargos declara��o Nilza                                                                                                                                                                                                  │completed│-        │
+│embargos.terceiro Juliano                                                                                                                                                                                                  │completed│-        │
+│a��o Ver�nica                                                                                                                                                                                                              │completed│-        │
+│Adicionar esportes na rotina semanal e nivelar alimenta��o kcal                                                                                                                                                            │later    │-        │
+│partilha tia rejane                                                                                                                                                                                                        │completed│-        │
+│investigar cumprimento de senten�a serasa                                                                                                                                                                                  │later    │-        │
+│Fazer escalada com a Raquel                                                                                                                                                                                                │completed│-        │
+│pingpong com a Raquel                                                                                                                                                                                                      │completed│-        │
+│浩汰湡慴⁲慭獩㈠爠晥楥獥瀠牯搠慩                                                                                                                                                                                           │later    │-        │
+│汢煯敵牡瀠Ᵽ猠湥慨瘠慩愠灰焠敵猠⃳潣獮杩捡獥慳⁲敤琠牡敤‮慭桮⃣⃩獥畴潤                                                                                                                                                            │later    │-        │
+│慦敺⁲潣獩獡焠敵洠⁥敤浥瀠慲敺⁲慰慲椠⁲畡敭瑮湡潤洠湩慨攠敮杲慩                                                                                                                                                               │later    │-        │
+│敲潳癬牥琠牯敮物⁡潣楺桮⁡楰杮湡潤                                                                                                                                                                                             │completed│-        │
+│灁汥濣丠汩慺                                                                                                                                                                                                              │completed│-        │
+│獅畴慤⁲牏ⵧ潭敤攠琠牥甠獥畱浥⁡潮琠祴                                                                                                                                                                                         │later    │-        │
+│inscri��o tjpe e tjpb                                                                                                                                                                                                      │completed│-        │
+│contratar secretariado remoto                                                                                                                                                                                              │completed│-        │
+│慆慬⁲潣潊猠扯敲搠瑡⁡畡楤湥楣⁡⁥潤⁣敤挠湯牰癯湡整                                                                                                                                                                              │completed│-        │
+│buscar dinheiro segundo turno                                                                                                                                                                                              │completed│-        │
+│enviar laudo para a Consuplan at� 6/nov                                                                                                                                                                                    │completed│-        │
+│敐楴楣湯牡瀠潲散獳潊杲⁥慰慲戠潬畱慥⁲楤桮楥潲                                                                                                                                                                               │completed│-        │
+│潢敬潴挠楲瑳慩潮                                                                                                                                                                                                           │completed│-        │
+│牴扡污慨⁲⹡敮牵灯慬瑳捩摩摡獥瀠牡⁡楶敶⁲慭獯愠瘠摩⁡⁥敳瑮                                                                                                                                                                       │later    │-        │
+│mandar proposta de trabalho para escrit�rios de BSB                                                                                                                                                                        │completed│-        │
+│Pedir meta fornecer foto perfil j� no caso latrocinio                                                                                                                                                                      │completed│-        │
+│certificado ipeatra                                                                                                                                                                                                        │later    │-        │
+│transferir instrucoes programas arch para crypt git                                                                                                                                                                        │completed│-        │
+│ideia empreender: chatbot que altera mensagem para parecer comunicacao nao violenta                                                                                                                                        │later    │-        │
+│浥牰敥摮摥牯獩潭›慢桮楥潲                                                                                                                                                                                                  │later    │-        │
+│Fazer backup 321 com parte arquivada e parte a disposicao                                                                                                                                                                  │later    │-        │
+│浥牰敥摮牥愠灰爠汥捡潩慮敭瑮獯                                                                                                                                                                                             │later    │-        │
+│䕍䅔⁓剐⁁〲㔲                                                                                                                                                                                                               │later    │-        │
+│mudar ativismo para modo 100% pragm�tico e silencioso                                                                                                                                                                      │later    │-        │
+│慦敺⁲硥牥楣牰灯獯潴瀠汥摥慵摲慰慲攠⁵⁥⁡慲畱汥                                                                                                                                                                               │later    │-        │
+│潣据汩慩⁲獥畴潤⁳久䍁‬䩔䝍‬剐䙏卅体⁒䙉卒䔠吠䱁䕖⁚䑁⁖䱃义䍉十                                                                                                                                                                  │completed│-        │
+│Coisas para fazer DOMINGO                                                                                                                                                                                                  │later    │-        │
+│9e6b98f7...                                                                                                                                                                                                                │later    │-        │
+│change tower concursos nao rastreados TJMG*, TJMT*, TJMS*, TJRN*, TJRS*, TJPB, TJES*, TJPE*, TJBA* - confirmar passagens e docs                                                                                            │completed│-        │
+│desbloquear tablet para audi�ncia de escolha tjpe E colocar serventias para criar na minha lista tjpe                                                                                                                      │completed│-        │
+│pedir pro GEMINI PRO montar um TODO com cada ponto do edital do TJRS com data prevista para fazer e tempo previsto de estudo para cada ponto, de forma a completar o conte�do do edital at� 28/06/26 estudando 4h por dia   │completed│-        │
+│procurar adv rec�m formado para assumir processos microsoft                                                                                                                                                                │completed│-        │
+│procurar psic�logo para laudo autismo (e estudar o da vev�)                                                                                                                                                                │later    │-        │
+│procurar psiquiatra conv�nio com RQE para laudo autismo NO TELEVITA CASSI                                                                                                                                                  │later    │-        │
+│潣灭慲⁲敬楧汳濣琠扪⁡敳渠潡搠牥瀠牡⁡浩牰浩物                                                                                                                                                                                 │later    │-        │
+│Marcar Otorrino                                                                                                                                                                                                            │completed│-        │
+│farmaciao: tiras nasais, shampoo caspa e icaden                                                                                                                                                                            │completed│-        │
+│Cadastrar INFO Push STF e STF                                                                                                                                                                                              │completed│-        │
+│慍捲牡搠湥楴瑳⁡慰慲椠慤愠瀠慯                                                                                                                                                                                               │completed│-        │
+│Procurar p�s ead monografia                                                                                                                                                                                                │later    │-        │
+│Comprar lentes �culos no aliexpress                                                                                                                                                                                        │later    │-        │
+│pegar quest�es do gemini e colocar nas questoes de prova oral                                                                                                                                                               │completed│-        │
+│verificar uso ssd                                                                                                                                                                                                          │completed│-        │
+│Compras EUA                                                                                                                                                                                                                │later    │-        │
+│敐楴楣湯牡愠䙉卒                                                                                                                                                                                                          │completed│-        │
+│Corrigir glibc do arch hdd5tb                                                                                                                                                                                              │completed│-        │
+│Cancelar passagens TJMT                                                                                                                                                                                                    │completed│-        │
+│Falar com vereador Guaiba                                                                                                                                                                                                  │later    │-        │
+│declara��es fiscais APED FV                                                                                                                                                                                                 │later    │-        │
+│敖⁲獩湥橴瑭                                                                                                                                                                                                               │completed│-        │
+│慐慧⁲䩔䅂                                                                                                                                                                                                                  │completed│-        │
+│敺慲⁲浥楡獬                                                                                                                                                                                                                │later    │-        │
+│慍捲牡攠慸敭漠潴牲湩潳潮                                                                                                                                                                                                   │completed│-        │
+│fazer backup termux e reinstalar o termux                                                                                                                                                                                  │completed│-        │
+│ver quest�o dinheiro indizacao em dolar                                                                                                                                                                                    │later    │-        │
+│dar sudo permitindo libvirtd domingo pra blocked e fim da manh� desfazendo sudo. Fazer isso para outras coisas que tu possa querer.                                                                                        │completed│-        │
+│remover liberacao browser celular                                                                                                                                                                                          │later    │-        │
+│b515e25f...                                                                                                                                                                                                                │later    │-        │
+│2873d267...                                                                                                                                                                                                                │later    │-        │
+└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴─────────┴─────────┘

+26 -9 internal/db/reader.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
diff --git a/internal/db/reader.go b/internal/db/reader.go
index d960b90..1ec314f 100644
--- a/internal/db/reader.go
+++ b/internal/db/reader.go
@@ -13,6 +13,7 @@ import (
 	"regexp"
 	"strings"
 	"unicode/utf16"
+	"unicode/utf8"
 
 	"github.com/syndtr/goleveldb/leveldb"
 	"github.com/syndtr/goleveldb/leveldb/opt"
@@ -89,11 +90,18 @@ func EnrichTask(taskID string) (string, error) {
 }
 
 func cleanName(s string) string {
-	b := []byte(s)
-	if len(b) == 0 {
+	if s == "" {
 		return ""
 	}
 
+	// If it's valid UTF-8 and contains no null bytes, it's probably fine
+	if utf8.ValidString(s) && !strings.Contains(s, "\x00") {
+		return s
+	}
+
+	b := []byte(s)
+
+	// Check for UTF-16LE pattern (interleaved nulls)
 	isUtf16 := false
 	if len(b) >= 2 {
 		nullCount := 0
@@ -107,16 +115,25 @@ func cleanName(s string) string {
 		}
 	}
 
-	if !isUtf16 {
-		return s
+	if isUtf16 && len(b)%2 == 0 {
+		utf16s := make([]uint16, len(b)/2)
+		for i := 0; i < len(utf16s); i++ {
+			utf16s[i] = binary.LittleEndian.Uint16(b[i*2:])
+		}
+		return string(utf16.Decode(utf16s))
 	}
 
-	utf16s := make([]uint16, len(b)/2)
-	for i := 0; i < len(utf16s); i++ {
-		utf16s[i] = binary.LittleEndian.Uint16(b[i*2:])
+	// If it's not UTF-16 but has invalid UTF-8, it's likely Latin-1 (ISO-8859-1)
+	if !utf8.ValidString(s) {
+		runes := make([]rune, len(b))
+		for i, v := range b {
+			runes[i] = rune(v)
+		}
+		return string(runes)
 	}
-	
-	return string(utf16.Decode(utf16s))
+
+	// Fallback: just remove null bytes if it's otherwise valid UTF-8
+	return strings.ReplaceAll(s, "\x00", "")
 }
 
 func isEncrypted(s string) bool {

+62 -19 cmd/task/list.go link
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
diff --git a/cmd/task/list.go b/cmd/task/list.go
index 8b56a4e..5b33423 100644
--- a/cmd/task/list.go
+++ b/cmd/task/list.go
@@ -47,6 +47,7 @@ are not available through the API. Only metadata is shown.`,
 func init() {
 	ListCmd.Flags().StringP("area", "a", "", "Filter by area key")
 	ListCmd.Flags().StringP("status", "s", "", "Filter by status")
+	ListCmd.Flags().StringP("scheduled", "d", "", "Filter by scheduled date (today, tomorrow, or YYYY-MM-DD)")
 	ListCmd.Flags().Bool("all", false, "Show all tasks including completed")
 	ListCmd.Flags().Bool("json", false, "Output as JSON")
 
@@ -87,8 +88,13 @@ func runList(cmd *cobra.Command, _ []string) error {
 		return err
 	}
 
+	scheduledFilter, err := resolveScheduledFilter(cmd)
+	if err != nil {
+		return err
+	}
+
 	showAll := mustGetBoolFlag(cmd, "all")
-	filtered := applyFiltersEnriched(enriched, areaID, statusFilter, showAll)
+	filtered := applyFiltersEnriched(enriched, areaID, statusFilter, scheduledFilter, showAll)
 
 	if len(filtered) == 0 {
 		fmt.Fprintln(cmd.OutOrStdout(), "No tasks found")
@@ -103,8 +109,6 @@ func runList(cmd *cobra.Command, _ []string) error {
 	return outputTableEnriched(cmd, filtered)
 }
 
-// mustGetStringFlag returns the string flag value. Panics if flag doesn't exist
-// (indicates a programming error—flags are defined in init).
 func mustGetStringFlag(cmd *cobra.Command, name string) string {
 	f := cmd.Flags().Lookup(name)
 	if f == nil {
@@ -114,7 +118,6 @@ func mustGetStringFlag(cmd *cobra.Command, name string) string {
 	return f.Value.String()
 }
 
-// mustGetBoolFlag returns the bool flag value. Panics if flag doesn't exist.
 func mustGetBoolFlag(cmd *cobra.Command, name string) bool {
 	f := cmd.Flags().Lookup(name)
 	if f == nil {
@@ -129,19 +132,15 @@ func resolveAreaFilter(cmd *cobra.Command) (string, error) {
 
 	cfg, err := config.Load()
 	if err != nil {
-		// Config not required if no area flag and we just skip default
 		if errors.Is(err, config.ErrNotFound) {
 			if areaKey != "" {
 				return "", err
 			}
-
 			return "", nil
 		}
-
 		return "", err
 	}
 
-	// Use default area if no explicit flag
 	if areaKey == "" {
 		areaKey = cfg.Defaults.Area
 	}
@@ -172,8 +171,30 @@ func resolveStatusFilter(cmd *cobra.Command) (string, error) {
 	return string(s), nil
 }
 
-func applyFiltersEnriched(tasks []enrichedTask, areaID, statusFilter string, showAll bool) []enrichedTask {
-	// Extract basic tasks for library filter
+func resolveScheduledFilter(cmd *cobra.Command) (string, error) {
+	val := mustGetStringFlag(cmd, "scheduled")
+	if val == "" {
+		return "", nil
+	}
+
+	today := time.Now().Format("2006-01-02")
+	tomorrow := time.Now().AddDate(0, 0, 1).Format("2006-01-02")
+
+	switch val {
+	case "today":
+		return today, nil
+	case "tomorrow":
+		return tomorrow, nil
+	default:
+		_, err := time.Parse("2006-01-02", val)
+		if err != nil {
+			return "", fmt.Errorf("invalid date format for --scheduled, use YYYY-MM-DD: %w", err)
+		}
+		return val, nil
+	}
+}
+
+func applyFiltersEnriched(tasks []enrichedTask, areaID, statusFilter, scheduledFilter string, showAll bool) []enrichedTask {
 	baseTasks := make([]lunatask.Task, len(tasks))
 	for i, t := range tasks {
 		baseTasks[i] = t.Task
@@ -193,9 +214,18 @@ func applyFiltersEnriched(tasks []enrichedTask, areaID, statusFilter string, sho
 
 	filteredBase := lunatask.FilterTasks(baseTasks, opts)
 	
-	// Map back to enriched
 	res := make([]enrichedTask, 0, len(filteredBase))
 	for _, fb := range filteredBase {
+		if scheduledFilter != "" {
+			taskDate := ""
+			if fb.ScheduledOn != nil {
+				taskDate = fb.ScheduledOn.Time.Format("2006-01-02")
+			}
+			if taskDate != scheduledFilter {
+				continue
+			}
+		}
+
 		for _, et := range tasks {
 			if et.ID == fb.ID {
 				res = append(res, et)
@@ -209,12 +239,7 @@ func applyFiltersEnriched(tasks []enrichedTask, areaID, statusFilter string, sho
 func outputJSONEnriched(cmd *cobra.Command, tasks []enrichedTask) error {
 	enc := json.NewEncoder(cmd.OutOrStdout())
 	enc.SetIndent("", "  ")
-
-	if err := enc.Encode(tasks); err != nil {
-		return fmt.Errorf("encoding JSON: %w", err)
-	}
-
-	return nil
+	return enc.Encode(tasks)
 }
 
 func outputTableEnriched(cmd *cobra.Command, tasks []enrichedTask) error {
@@ -247,11 +272,29 @@ func outputTableEnriched(cmd *cobra.Command, tasks []enrichedTask) error {
 				return ui.TableHeaderStyle()
 			}
 
+			if row >= 0 && row < len(tasks) {
+				t := tasks[row]
+				if t.Status != nil {
+					switch *t.Status {
+					case lunatask.StatusLater:
+						return ui.StatusLater
+					case lunatask.StatusNext:
+						return ui.StatusNext
+					case lunatask.StatusInProgress:
+						return ui.StatusInProgress
+					case lunatask.StatusWaiting:
+						return ui.StatusWaiting
+					case lunatask.StatusCompleted:
+						return ui.StatusCompleted
+					}
+				}
+			}
+
 			return lipgloss.NewStyle()
 		}).
-		Border(ui.TableBorder())
+		Border(ui.TableBorder()).
+		BorderRow(true)
 
 	fmt.Fprintln(cmd.OutOrStdout(), tbl.Render())
-
 	return nil
 }
+9 -0 internal/ui/styles.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
diff --git a/internal/ui/styles.go b/internal/ui/styles.go
index cc2b395..878dee8 100644
--- a/internal/ui/styles.go
+++ b/internal/ui/styles.go
@@ -61,6 +61,15 @@ var (
 		Padding(0, 1)}
 )
 
+// Task status colors.
+var (
+	StatusLater      = lipgloss.NewStyle().Foreground(lipgloss.Color("8"))  // Gray
+	StatusNext       = lipgloss.NewStyle().Foreground(lipgloss.Color("12")) // Light Blue
+	StatusInProgress = lipgloss.NewStyle().Foreground(lipgloss.Color("4"))  // Blue
+	StatusWaiting    = lipgloss.NewStyle().Foreground(lipgloss.Color("5"))  // Magenta
+	StatusCompleted  = lipgloss.NewStyle().Foreground(lipgloss.Color("2"))  // Green
+)
+
 // FormatDate formats a time.Time as a date string using the user's locale.
 // Locale is auto-detected from LC_TIME, LC_ALL, or LANG environment variables.
 func FormatDate(t time.Time) string {

+11 -3 cmd/init/init.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
diff --git a/cmd/init/init.go b/cmd/init/init.go
index e9bd37e..b7bde0c 100644
--- a/cmd/init/init.go
+++ b/cmd/init/init.go
@@ -50,6 +50,7 @@ This command will guide you through:
   - Adding areas, goals, notebooks, and habits from Lunatask
   - Setting default area and notebook
   - Configuring and verifying your access token
+  - Setting your master password for proactive local database enrichment
 
 Use --generate-config to create an example config file for manual editing
 when running non-interactively.`,
@@ -148,9 +149,10 @@ func printWelcome(cmd *cobra.Command) {
 	fmt.Fprintln(out)
 	fmt.Fprintln(out, "This wizard will help you configure lune for use with Lunatask.")
 	fmt.Fprintln(out)
-	fmt.Fprintln(out, "Since Lunatask is end-to-end encrypted, lune can't fetch your")
-	fmt.Fprintln(out, "areas, goals, notebooks, or habits automatically. You'll need to")
-	fmt.Fprintln(out, "copy IDs from the Lunatask app.")
+	fmt.Fprintln(out, "Since Lunatask is end-to-end encrypted, lune needs some initial")
+	fmt.Fprintln(out, "configuration to map your items. You can also provide your")
+	fmt.Fprintln(out, "Master Password to enable proactive local database enrichment,")
+	fmt.Fprintln(out, "which allows lune to see your task and note names automatically.")
 	fmt.Fprintln(out)
 	fmt.Fprintln(out, ui.Bold.Render("Where to find IDs:"))
 	fmt.Fprintln(out, "  Open any item's settings modal → click 'Copy [Item] ID' (bottom left)")
@@ -242,6 +244,7 @@ func printConfigSummary(out io.Writer, cfg *config.Config) {
 	}
 
 	fmt.Fprintf(out, "  Color: %s\n", color)
+	fmt.Fprintf(out, "  LocalDB: %v\n", cfg.Experimental.LocalDB)
 	fmt.Fprintln(out)
 }
 
@@ -331,6 +334,11 @@ color = "auto"
 # id = "00000000-0000-0000-0000-000000000000"
 # name = "Exercise"
 # key = "exercise"
+
+# Experimental features
+[experimental]
+# Enable proactive local database enrichment to see real task/note names
+local_db = true
 `
 
 func runGenerateConfig(cmd *cobra.Command) error {
+4 -2 cmd/note/list.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
diff --git a/cmd/note/list.go b/cmd/note/list.go
index 99783a0..5f9bba4 100644
--- a/cmd/note/list.go
+++ b/cmd/note/list.go
@@ -31,8 +31,10 @@ var ListCmd = &cobra.Command{
 	Short: "List notes",
 	Long: `List notes from Lunatask.
 
-Note: Due to end-to-end encryption, note names and content
-are not available through the API. Only metadata is shown.`,
+Proactive Features:
+  - Local Database Enrichment: Retrieves real note and notebook names 
+    from your local database using your master password.
+  - Organized View: Shows notes grouped by their original notebooks.`,
 	RunE: runList,
 }
 
+5 -2 cmd/task/list.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
diff --git a/cmd/task/list.go b/cmd/task/list.go
index 5b33423..b6b876d 100644
--- a/cmd/task/list.go
+++ b/cmd/task/list.go
@@ -39,8 +39,11 @@ var ListCmd = &cobra.Command{
 By default, shows incomplete tasks and tasks completed today.
 Use --all to show entire task history.
 
-Note: Due to end-to-end encryption, task names and notes
-are not available through the API. Only metadata is shown.`,
+Proactive Features:
+  - Local Database Enrichment: If enabled in config and master password is set, 
+    real task names are retrieved from your local Lunatask database.
+  - Status Coloring: Tasks are color-coded by their current status.
+  - Scheduled Filtering: Filter tasks by when they are planned (today, tomorrow, or date).`,
 	RunE: runList,
 }
 

+51 -10 cmd/area/list.go link
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
diff --git a/cmd/area/list.go b/cmd/area/list.go
index e634763..4413dc9 100644
--- a/cmd/area/list.go
+++ b/cmd/area/list.go
@@ -9,20 +9,29 @@ import (
 	"fmt"
 
 	"git.secluded.site/lune/internal/config"
+	"git.secluded.site/lune/internal/db"
 	"git.secluded.site/lune/internal/ui"
 	"github.com/charmbracelet/lipgloss"
 	"github.com/charmbracelet/lipgloss/table"
 	"github.com/spf13/cobra"
 )
 
+type displayArea struct {
+	ID    string        `json:"id"`
+	Key   string        `json:"key"`
+	Name  string        `json:"name"`
+	Goals []config.Goal `json:"goals"`
+}
+
 // ListCmd lists configured areas and their goals.
 var ListCmd = &cobra.Command{
 	Use:   "list",
-	Short: "List configured areas",
-	Long: `List areas configured in lune.
+	Short: "List areas",
+	Long: `List areas configured in lune or found in your local database.
 
-Areas are copied from the Lunatask desktop app during 'lune init'.
-Each area may have goals associated with it.`,
+Proactive Features:
+  - Local Database Discovery: Automatically lists all areas from your Lunatask
+    database if experimental mode is enabled.`,
 	RunE: runList,
 }
 
@@ -36,21 +45,53 @@ func runList(cmd *cobra.Command, _ []string) error {
 		return err
 	}
 
-	if len(cfg.Areas) == 0 {
-		fmt.Fprintln(cmd.OutOrStdout(), "No areas configured")
+	areas := make([]displayArea, 0)
+	for _, a := range cfg.Areas {
+		areas = append(areas, displayArea{
+			ID:    a.ID,
+			Key:   a.Key,
+			Name:  a.Name,
+			Goals: a.Goals,
+		})
+	}
+
+	if cfg.Experimental.LocalDB {
+		localAreas, _ := db.ListLocalAreas()
+		for _, la := range localAreas {
+			found := false
+			for i, a := range areas {
+				if a.ID == la.ID {
+					areas[i].Name = la.Name // Use real name from DB
+					found = true
+					break
+				}
+			}
+			if !found {
+				areas = append(areas, displayArea{
+					ID:    la.ID,
+					Key:   "-", // No key assigned yet
+					Name:  la.Name,
+					Goals: nil,
+				})
+			}
+		}
+	}
+
+	if len(areas) == 0 {
+		fmt.Fprintln(cmd.OutOrStdout(), "No areas found")
 
 		return nil
 	}
 
 	jsonFlag, _ := cmd.Flags().GetBool("json")
 	if jsonFlag {
-		return outputJSON(cmd, cfg.Areas)
+		return outputJSON(cmd, areas)
 	}
 
-	return outputTable(cmd, cfg.Areas)
+	return outputTable(cmd, areas)
 }
 
-func outputJSON(cmd *cobra.Command, areas []config.Area) error {
+func outputJSON(cmd *cobra.Command, areas []displayArea) error {
 	enc := json.NewEncoder(cmd.OutOrStdout())
 	enc.SetIndent("", "  ")
 
@@ -61,7 +102,7 @@ func outputJSON(cmd *cobra.Command, areas []config.Area) error {
 	return nil
 }
 
-func outputTable(cmd *cobra.Command, areas []config.Area) error {
+func outputTable(cmd *cobra.Command, areas []displayArea) error {
 	rows := make([][]string, 0, len(areas))
 	truncated := false
 
+93 -0 internal/db/reader.go link
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
diff --git a/internal/db/reader.go b/internal/db/reader.go
index 1ec314f..c84a785 100644
--- a/internal/db/reader.go
+++ b/internal/db/reader.go
@@ -89,6 +89,99 @@ func EnrichTask(taskID string) (string, error) {
 	return "", nil
 }
 
+// Area represents a Lunatask area found in the local database.
+type Area struct {
+	ID   string
+	Name string
+}
+
+// ListLocalAreas scans the database for all areas using V8-serialized property matching.
+func ListLocalAreas() ([]Area, error) {
+	dbPath := GetDefaultPath()
+	if _, err := os.Stat(dbPath); os.IsNotExist(err) {
+		return nil, nil
+	}
+
+	o := &opt.Options{
+		Comparer: idbComparer{},
+		ReadOnly: true,
+	}
+
+	db, err := leveldb.OpenFile(dbPath, o)
+	if err != nil {
+		return nil, err
+	}
+	defer db.Close()
+
+	iter := db.NewIterator(nil, nil)
+	defer iter.Release()
+
+	areas := make([]Area, 0)
+	for iter.Next() {
+		val := iter.Value()
+		
+		// In V8 serialization, "modelType" is 22 09 6d 6f 64 65 6c 54 79 70 65
+		// and "area" is 22 04 61 72 65 61
+		if bytes.Contains(val, []byte("\x22\x09modelType")) && bytes.Contains(val, []byte("\x22\x04area")) {
+			// Find the JSON blob in the "data" property
+			dataIdx := bytes.Index(val, []byte("\x22\x04data"))
+			if dataIdx != -1 {
+				start := bytes.IndexByte(val[dataIdx:], '{')
+				if start != -1 {
+					start += dataIdx
+					end := bytes.LastIndexByte(val, '}')
+					if end > start {
+						jsonBytes := val[start : end+1]
+						id := extractField(jsonBytes, "id")
+						name := extractField(jsonBytes, "name")
+						if id != "" && name != "" {
+							areas = append(areas, Area{
+								ID:   id,
+								Name: cleanName(name),
+							})
+						}
+					}
+				}
+			}
+		}
+	}
+	return areas, nil
+}
+
+func truncateString(s string, n int) string {
+	if len(s) > n {
+		return s[:n] + "..."
+	}
+	return s
+}
+
+func extractFieldFromString(data, field string) string {
+	re := regexp.MustCompile(`"` + field + `"\s*:\s*"([^"]+)"`)
+	matches := re.FindSubmatch([]byte(data))
+	if len(matches) > 1 {
+		return string(matches[1])
+	}
+	return ""
+}
+
+func toUtf16(s string) []byte {
+	res := make([]byte, 0, len(s)*2)
+	for _, r := range s {
+		res = append(res, byte(r), 0)
+	}
+	return res
+}
+
+func extractField(data []byte, field string) string {
+	// Extract field using regex for better reliability in serialized JSON
+	re := regexp.MustCompile(`"` + field + `"\s*:\s*"([^"]+)"`)
+	matches := re.FindSubmatch(data)
+	if len(matches) > 1 {
+		return string(matches[1])
+	}
+	return ""
+}
+
 func cleanName(s string) string {
 	if s == "" {
 		return ""

+56 -10 cmd/task/list.go link
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
diff --git a/cmd/task/list.go b/cmd/task/list.go
index b6b876d..768b0a9 100644
--- a/cmd/task/list.go
+++ b/cmd/task/list.go
@@ -59,6 +59,11 @@ func init() {
 }
 
 func runList(cmd *cobra.Command, _ []string) error {
+	cfg, _ := config.Load()
+	if cfg != nil && cfg.Experimental.LocalDB {
+		_ = db.WarmCache()
+	}
+
 	apiClient, err := client.New()
 	if err != nil {
 		return err
@@ -71,7 +76,6 @@ func runList(cmd *cobra.Command, _ []string) error {
 		return err
 	}
 
-	cfg, _ := config.Load()
 	enriched := make([]enrichedTask, 0, len(tasks))
 	for _, t := range tasks {
 		name := ""
@@ -246,6 +250,7 @@ func outputJSONEnriched(cmd *cobra.Command, tasks []enrichedTask) error {
 }
 
 func outputTableEnriched(cmd *cobra.Command, tasks []enrichedTask) error {
+	cfg, _ := config.Load()
 	rows := make([][]string, 0, len(tasks))
 
 	for _, task := range tasks {
@@ -264,36 +269,77 @@ func outputTableEnriched(cmd *cobra.Command, tasks []enrichedTask) error {
 			name = task.ID[:8] + "..."
 		}
 
-		rows = append(rows, []string{name, status, scheduled})
+		// Resolve Area name
+		area := "-"
+		if task.AreaID != nil {
+			area = *task.AreaID
+			if cfg != nil {
+				if a := cfg.AreaByID(*task.AreaID); a != nil {
+					area = a.Key
+				} else if cfg.Experimental.LocalDB {
+					// Proactive area lookup in local DB
+					if dbName, _ := db.EnrichTask(*task.AreaID); dbName != "" {
+						area = dbName
+					}
+				}
+			}
+		}
+
+		// Truncate name and area for clean table layout
+		if len(name) > 50 {
+			name = name[:47] + "..."
+		}
+		if len(area) > 20 {
+			area = area[:17] + "..."
+		}
+
+		rows = append(rows, []string{task.ID[:8], name, status, scheduled, area})
 	}
 
 	tbl := table.New().
-		Headers("NAME", "STATUS", "SCHEDULED").
+		Headers("ID", "NAME", "STATUS", "SCHEDULED", "AREA").
 		Rows(rows...).
 		StyleFunc(func(row, col int) lipgloss.Style {
+			style := lipgloss.NewStyle()
+
+			// Set column widths via style
+			switch col {
+			case 0: // ID
+				style = style.Width(10)
+			case 1: // NAME
+				style = style.Width(52)
+			case 2: // STATUS
+				style = style.Width(12)
+			case 3: // SCHEDULED
+				style = style.Width(12)
+			case 4: // AREA
+				style = style.Width(22)
+			}
+
 			if row == table.HeaderRow {
-				return ui.TableHeaderStyle()
+				return style.Inherit(ui.TableHeaderStyle())
 			}
 
+			// Color logic based on status
 			if row >= 0 && row < len(tasks) {
 				t := tasks[row]
 				if t.Status != nil {
 					switch *t.Status {
 					case lunatask.StatusLater:
-						return ui.StatusLater
+						style = style.Inherit(ui.StatusLater)
 					case lunatask.StatusNext:
-						return ui.StatusNext
+						style = style.Inherit(ui.StatusNext)
 					case lunatask.StatusInProgress:
-						return ui.StatusInProgress
+						style = style.Inherit(ui.StatusInProgress)
 					case lunatask.StatusWaiting:
-						return ui.StatusWaiting
+						style = style.Inherit(ui.StatusWaiting)
 					case lunatask.StatusCompleted:
-						return ui.StatusCompleted
+						style = style.Inherit(ui.StatusCompleted)
 					}
 				}
 			}
 
-			return lipgloss.NewStyle()
+			return style
 		}).
 		Border(ui.TableBorder()).
 		BorderRow(true)
+46 -9 internal/db/reader.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
diff --git a/internal/db/reader.go b/internal/db/reader.go
index c84a785..c0434d9 100644
--- a/internal/db/reader.go
+++ b/internal/db/reader.go
@@ -56,10 +56,20 @@ func SetMasterPassword(password string) error {
 	return os.WriteFile(path, f, 0600)
 }
 
-func EnrichTask(taskID string) (string, error) {
+var (
+	nameCache = make(map[string]string)
+	cacheWarmed = false
+)
+
+// WarmCache loads all names (tasks, notes, areas, notebooks) into memory.
+func WarmCache() error {
+	if cacheWarmed {
+		return nil
+	}
+
 	dbPath := GetDefaultPath()
 	if _, err := os.Stat(dbPath); os.IsNotExist(err) {
-		return "", nil
+		return nil
 	}
 
 	o := &opt.Options{
@@ -69,7 +79,7 @@ func EnrichTask(taskID string) (string, error) {
 
 	db, err := leveldb.OpenFile(dbPath, o)
 	if err != nil {
-		return "", err
+		return err
 	}
 	defer db.Close()
 
@@ -78,15 +88,42 @@ func EnrichTask(taskID string) (string, error) {
 
 	for iter.Next() {
 		val := iter.Value()
-		if bytes.Contains(val, []byte(taskID)) {
-			re := regexp.MustCompile(`"(title|name)"\s*:\s*"([^"]+)"`)
-			matches := re.FindSubmatch(val)
-			if len(matches) > 2 {
-				return cleanName(string(matches[2])), nil
+		// Try to find JSON-like blobs
+		if bytes.Contains(val, []byte("\"id\"")) && bytes.Contains(val, []byte("\"name\"")) {
+			start := bytes.IndexByte(val, '{')
+			end := bytes.LastIndexByte(val, '}')
+			if start != -1 && end != -1 && end > start {
+				jsonBytes := val[start : end+1]
+				id := extractField(jsonBytes, "id")
+				name := extractField(jsonBytes, "name")
+				if id != "" && name != "" {
+					nameCache[id] = cleanName(name)
+				}
+			}
+		} else if bytes.Contains(val, []byte("\"id\"")) && bytes.Contains(val, []byte("\"title\"")) {
+			// Notes use "title"
+			start := bytes.IndexByte(val, '{')
+			end := bytes.LastIndexByte(val, '}')
+			if start != -1 && end != -1 && end > start {
+				jsonBytes := val[start : end+1]
+				id := extractField(jsonBytes, "id")
+				title := extractField(jsonBytes, "title")
+				if id != "" && title != "" {
+					nameCache[id] = cleanName(title)
+				}
 			}
 		}
 	}
-	return "", nil
+	cacheWarmed = true
+	return nil
+}
+
+// EnrichTask attempts to find the task or note name in the cache or local database.
+func EnrichTask(taskID string) (string, error) {
+	if !cacheWarmed {
+		_ = WarmCache()
+	}
+	return nameCache[taskID], nil
 }
 
 // Area represents a Lunatask area found in the local database.

+5 -3 cmd/area/list.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
diff --git a/cmd/area/list.go b/cmd/area/list.go
index 4413dc9..ee7bc66 100644
--- a/cmd/area/list.go
+++ b/cmd/area/list.go
@@ -25,14 +25,16 @@ type displayArea struct {
 
 // ListCmd lists configured areas and their goals.
 var ListCmd = &cobra.Command{
-	Use:   "list",
-	Short: "List areas",
+	Use:     "list",
+	Aliases: []string{"lista"},
+	Short:   "List areas",
 	Long: `List areas configured in lune or found in your local database.
 
 Proactive Features:
   - Local Database Discovery: Automatically lists all areas from your Lunatask
     database if experimental mode is enabled.`,
-	RunE: runList,
+	RunE:         runList,
+	SilenceUsage: true,
 }
 
 func init() {
+48 -40 cmd/goal/list.go link
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
diff --git a/cmd/goal/list.go b/cmd/goal/list.go
index a206f0d..4122408 100644
--- a/cmd/goal/list.go
+++ b/cmd/goal/list.go
@@ -10,6 +10,7 @@ import (
 
 	"git.secluded.site/lune/internal/completion"
 	"git.secluded.site/lune/internal/config"
+	"git.secluded.site/lune/internal/db"
 	"git.secluded.site/lune/internal/ui"
 	"github.com/charmbracelet/lipgloss"
 	"github.com/charmbracelet/lipgloss/table"
@@ -18,13 +19,16 @@ import (
 
 // ListCmd lists configured goals.
 var ListCmd = &cobra.Command{
-	Use:   "list",
-	Short: "List configured goals",
-	Long: `List goals configured in lune.
+	Use:     "list [AREA]",
+	Aliases: []string{"lista"},
+	Short:   "List goals",
+	Long: `List goals configured in lune or found in your local database.
 
 By default, shows goals from the default area (if configured).
-Use --area to specify a different area, or --all to show all goals.`,
-	RunE: runList,
+Use --area or provide the area name as an argument to filter.`,
+	Args:         cobra.MaximumNArgs(1),
+	RunE:         runList,
+	SilenceUsage: true,
 }
 
 func init() {
@@ -35,30 +39,55 @@ func init() {
 	_ = ListCmd.RegisterFlagCompletionFunc("area", completion.Areas)
 }
 
-func runList(cmd *cobra.Command, _ []string) error {
+func runList(cmd *cobra.Command, args []string) error {
 	cfg, err := config.Load()
 	if err != nil {
 		return err
 	}
 
-	areaKey, _ := cmd.Flags().GetString("area")
-	showAll, _ := cmd.Flags().GetBool("all")
-	usingDefault := areaKey == "" && !showAll && cfg.Defaults.Area != ""
-
-	areas := filterAreas(cfg, areaKey, showAll)
-	if len(areas) == 0 {
-		fmt.Fprintln(cmd.OutOrStdout(), "No areas found")
+	if cfg != nil && cfg.Experimental.LocalDB {
+		_ = db.WarmCache()
+	}
 
-		return nil
+	areaInput := ""
+	if len(args) > 0 {
+		areaInput = args[0]
+	} else {
+		areaInput, _ = cmd.Flags().GetString("area")
 	}
 
-	goals := collectGoals(areas)
-	if len(goals) == 0 {
-		if usingDefault {
-			fmt.Fprintf(cmd.OutOrStdout(), "No goals configured in %s area\n", cfg.Defaults.Area)
+	showAll, _ := cmd.Flags().GetBool("all")
+	
+	var areas []config.Area
+	if showAll {
+		areas = cfg.Areas
+	} else {
+		targetAreaID := ""
+		if areaInput == "" && cfg.Defaults.Area != "" {
+			areaInput = cfg.Defaults.Area
+		}
+
+		if areaInput != "" {
+			// Try config
+			if a := cfg.AreaByKey(areaInput); a != nil {
+				targetAreaID = a.ID
+				areas = []config.Area{*a}
+			} else {
+				// Try proactive DB
+				targetAreaID = db.FindAreaIDByName(areaInput)
+				if targetAreaID != "" {
+					// We found an area in DB that isn't in config, 
+					// we create a temporary config.Area to show its goals
+					areas = []config.Area{{ID: targetAreaID, Name: areaInput, Key: "-"}}
+				}
+			}
 		} else {
-			fmt.Fprintln(cmd.OutOrStdout(), "No goals configured")
+			areas = cfg.Areas
 		}
+	}
+	goals := collectGoals(areas)
+	if len(goals) == 0 {
+		fmt.Fprintln(cmd.OutOrStdout(), "No goals configured")
 
 		return nil
 	}
@@ -78,27 +107,6 @@ type goalWithArea struct {
 	AreaKey string `json:"area_key"`
 }
 
-func filterAreas(cfg *config.Config, areaKey string, showAll bool) []config.Area {
-	if showAll {
-		return cfg.Areas
-	}
-
-	if areaKey == "" {
-		areaKey = cfg.Defaults.Area
-	}
-
-	if areaKey == "" {
-		return cfg.Areas
-	}
-
-	area := cfg.AreaByKey(areaKey)
-	if area == nil {
-		return nil
-	}
-
-	return []config.Area{*area}
-}
-
 func collectGoals(areas []config.Area) []goalWithArea {
 	var goals []goalWithArea
 
+5 -3 cmd/note/list.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
diff --git a/cmd/note/list.go b/cmd/note/list.go
index 5f9bba4..219d321 100644
--- a/cmd/note/list.go
+++ b/cmd/note/list.go
@@ -27,15 +27,17 @@ type enrichedNote struct {
 
 // ListCmd lists notes. Exported for potential use by shortcuts.
 var ListCmd = &cobra.Command{
-	Use:   "list",
-	Short: "List notes",
+	Use:     "list",
+	Aliases: []string{"lista"},
+	Short:   "List notes",
 	Long: `List notes from Lunatask.
 
 Proactive Features:
   - Local Database Enrichment: Retrieves real note and notebook names 
     from your local database using your master password.
   - Organized View: Shows notes grouped by their original notebooks.`,
-	RunE: runList,
+	RunE:         runList,
+	SilenceUsage: true,
 }
 
 func init() {
+40 -27 cmd/task/list.go link
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
diff --git a/cmd/task/list.go b/cmd/task/list.go
index 768b0a9..5156fc4 100644
--- a/cmd/task/list.go
+++ b/cmd/task/list.go
@@ -8,11 +8,11 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	"strings"
 	"time"
 
 	"git.secluded.site/go-lunatask"
 	"git.secluded.site/lune/internal/client"
-	"git.secluded.site/lune/internal/completion"
 	"git.secluded.site/lune/internal/config"
 	"git.secluded.site/lune/internal/db"
 	"git.secluded.site/lune/internal/ui"
@@ -32,19 +32,26 @@ var ErrUnknownArea = errors.New("unknown area key")
 
 // ListCmd lists tasks. Exported for potential use by shortcuts.
 var ListCmd = &cobra.Command{
-	Use:   "list",
-	Short: "List tasks",
+	Use:     "list [AREA]",
+	Aliases: []string{"lista"},
+	Short:   "List tasks",
 	Long: `List tasks from Lunatask.
 
 By default, shows incomplete tasks and tasks completed today.
 Use --all to show entire task history.
 
+You can filter by area by providing its name as an argument 
+or using the --area flag.
+
 Proactive Features:
   - Local Database Enrichment: If enabled in config and master password is set, 
     real task names are retrieved from your local Lunatask database.
   - Status Coloring: Tasks are color-coded by their current status.
-  - Scheduled Filtering: Filter tasks by when they are planned (today, tomorrow, or date).`,
-	RunE: runList,
+  - Scheduled Filtering: Filter tasks by when they are planned (today, tomorrow, or date).
+  - Proactive Area Filtering: Filter by area names found in your local database.`,
+	Args:         cobra.MaximumNArgs(1),
+	RunE:         runList,
+	SilenceUsage: true,
 }
 
 func init() {
@@ -53,12 +60,9 @@ func init() {
 	ListCmd.Flags().StringP("scheduled", "d", "", "Filter by scheduled date (today, tomorrow, or YYYY-MM-DD)")
 	ListCmd.Flags().Bool("all", false, "Show all tasks including completed")
 	ListCmd.Flags().Bool("json", false, "Output as JSON")
-
-	_ = ListCmd.RegisterFlagCompletionFunc("area", completion.Areas)
-	_ = ListCmd.RegisterFlagCompletionFunc("status", completion.TaskStatuses)
 }
 
-func runList(cmd *cobra.Command, _ []string) error {
+func runList(cmd *cobra.Command, args []string) error {
 	cfg, _ := config.Load()
 	if cfg != nil && cfg.Experimental.LocalDB {
 		_ = db.WarmCache()
@@ -85,10 +89,11 @@ func runList(cmd *cobra.Command, _ []string) error {
 		enriched = append(enriched, enrichedTask{Task: t, Name: name})
 	}
 
-	areaID, err := resolveAreaFilter(cmd)
+	areaID, err := resolveAreaFilterSmart(cmd, args)
 	if err != nil {
 		return err
 	}
+// ...
 
 	statusFilter, err := resolveStatusFilter(cmd)
 	if err != nil {
@@ -134,34 +139,42 @@ func mustGetBoolFlag(cmd *cobra.Command, name string) bool {
 	return f.Value.String() == "true"
 }
 
-func resolveAreaFilter(cmd *cobra.Command) (string, error) {
-	areaKey := mustGetStringFlag(cmd, "area")
+func resolveAreaFilterSmart(cmd *cobra.Command, args []string) (string, error) {
+	areaInput := ""
+	if len(args) > 0 {
+		areaInput = args[0]
+	} else {
+		areaInput = mustGetStringFlag(cmd, "area")
+	}
 
-	cfg, err := config.Load()
-	if err != nil {
-		if errors.Is(err, config.ErrNotFound) {
-			if areaKey != "" {
-				return "", err
-			}
+	if areaInput == "" {
+		cfg, err := config.Load()
+		if err == nil && cfg.Defaults.Area != "" {
+			areaInput = cfg.Defaults.Area
+		} else {
 			return "", nil
 		}
-		return "", err
 	}
 
-	if areaKey == "" {
-		areaKey = cfg.Defaults.Area
+	cfg, err := config.Load()
+	if err == nil {
+		area := cfg.AreaByKey(areaInput)
+		if area != nil {
+			return area.ID, nil
+		}
 	}
 
-	if areaKey == "" {
-		return "", nil
+	// Try local DB (proactive)
+	if id := db.FindAreaIDByName(areaInput); id != "" {
+		return id, nil
 	}
 
-	area := cfg.AreaByKey(areaKey)
-	if area == nil {
-		return "", fmt.Errorf("%w: %s", ErrUnknownArea, areaKey)
+	// Fallback to error if it's not a UUID
+	if len(areaInput) == 36 && strings.Count(areaInput, "-") == 4 {
+		return areaInput, nil
 	}
 
-	return area.ID, nil
+	return "", fmt.Errorf("%w: %s", ErrUnknownArea, areaInput)
 }
 
 func resolveStatusFilter(cmd *cobra.Command) (string, error) {
+22 -1 internal/db/reader.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
diff --git a/internal/db/reader.go b/internal/db/reader.go
index c0434d9..93311ff 100644
--- a/internal/db/reader.go
+++ b/internal/db/reader.go
@@ -118,7 +118,28 @@ func WarmCache() error {
 	return nil
 }
 
-// EnrichTask attempts to find the task or note name in the cache or local database.
+// FindAreaIDByName searches for an area ID by its name in the cache.
+func FindAreaIDByName(name string) string {
+	if !cacheWarmed {
+		_ = WarmCache()
+	}
+	
+	nameLower := strings.ToLower(name)
+	for id, n := range nameCache {
+		if strings.ToLower(n) == nameLower {
+			return id
+		}
+	}
+	// Try partial match if no exact match
+	for id, n := range nameCache {
+		if strings.Contains(strings.ToLower(n), nameLower) {
+			return id
+		}
+	}
+	return ""
+}
+
+// EnrichTask attempts to find the task or note name in the cache.
 func EnrichTask(taskID string) (string, error) {
 	if !cacheWarmed {
 		_ = WarmCache()

+5 -9 internal/db/reader.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
diff --git a/internal/db/reader.go b/internal/db/reader.go
index 93311ff..834e8b4 100644
--- a/internal/db/reader.go
+++ b/internal/db/reader.go
@@ -245,11 +245,6 @@ func cleanName(s string) string {
 		return ""
 	}
 
-	// If it's valid UTF-8 and contains no null bytes, it's probably fine
-	if utf8.ValidString(s) && !strings.Contains(s, "\x00") {
-		return s
-	}
-
 	b := []byte(s)
 
 	// Check for UTF-16LE pattern (interleaved nulls)
@@ -271,11 +266,12 @@ func cleanName(s string) string {
 		for i := 0; i < len(utf16s); i++ {
 			utf16s[i] = binary.LittleEndian.Uint16(b[i*2:])
 		}
-		return string(utf16.Decode(utf16s))
+		s = string(utf16.Decode(utf16s))
+		b = []byte(s)
 	}
 
-	// If it's not UTF-16 but has invalid UTF-8, it's likely Latin-1 (ISO-8859-1)
-	if !utf8.ValidString(s) {
+	// Now check if it's valid UTF-8. If not, it's likely Latin-1 (ISO-8859-1)
+	if !utf8.Valid(b) {
 		runes := make([]rune, len(b))
 		for i, v := range b {
 			runes[i] = rune(v)
@@ -283,7 +279,7 @@ func cleanName(s string) string {
 		return string(runes)
 	}
 
-	// Fallback: just remove null bytes if it's otherwise valid UTF-8
+	// Even if it is valid UTF-8, it might contain null bytes from a messy extraction
 	return strings.ReplaceAll(s, "\x00", "")
 }
 

+0 -1 cmd/area/list.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
diff --git a/cmd/area/list.go b/cmd/area/list.go
index ee7bc66..72ab397 100644
--- a/cmd/area/list.go
+++ b/cmd/area/list.go
@@ -26,7 +26,6 @@ type displayArea struct {
 // ListCmd lists configured areas and their goals.
 var ListCmd = &cobra.Command{
 	Use:     "list",
-	Aliases: []string{"lista"},
 	Short:   "List areas",
 	Long: `List areas configured in lune or found in your local database.
 
+0 -1 cmd/goal/list.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
diff --git a/cmd/goal/list.go b/cmd/goal/list.go
index 4122408..c11192a 100644
--- a/cmd/goal/list.go
+++ b/cmd/goal/list.go
@@ -20,7 +20,6 @@ import (
 // ListCmd lists configured goals.
 var ListCmd = &cobra.Command{
 	Use:     "list [AREA]",
-	Aliases: []string{"lista"},
 	Short:   "List goals",
 	Long: `List goals configured in lune or found in your local database.
 
+0 -1 cmd/note/list.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
diff --git a/cmd/note/list.go b/cmd/note/list.go
index 219d321..dcdad56 100644
--- a/cmd/note/list.go
+++ b/cmd/note/list.go
@@ -28,7 +28,6 @@ type enrichedNote struct {
 // ListCmd lists notes. Exported for potential use by shortcuts.
 var ListCmd = &cobra.Command{
 	Use:     "list",
-	Aliases: []string{"lista"},
 	Short:   "List notes",
 	Long: `List notes from Lunatask.
 
+36 -1 cmd/task/list.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
diff --git a/cmd/task/list.go b/cmd/task/list.go
index 5156fc4..b6b24b6 100644
--- a/cmd/task/list.go
+++ b/cmd/task/list.go
@@ -8,6 +8,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	"sort"
 	"strings"
 	"time"
 
@@ -33,7 +34,6 @@ var ErrUnknownArea = errors.New("unknown area key")
 // ListCmd lists tasks. Exported for potential use by shortcuts.
 var ListCmd = &cobra.Command{
 	Use:     "list [AREA]",
-	Aliases: []string{"lista"},
 	Short:   "List tasks",
 	Long: `List tasks from Lunatask.
 
@@ -118,6 +118,8 @@ func runList(cmd *cobra.Command, args []string) error {
 		return outputJSONEnriched(cmd, filtered)
 	}
 
+	sortTasks(filtered)
+
 	return outputTableEnriched(cmd, filtered)
 }
 
@@ -360,3 +362,36 @@ func outputTableEnriched(cmd *cobra.Command, tasks []enrichedTask) error {
 	fmt.Fprintln(cmd.OutOrStdout(), tbl.Render())
 	return nil
 }
+
+func sortTasks(tasks []enrichedTask) {
+	priority := map[lunatask.TaskStatus]int{
+		lunatask.StatusInProgress: 1,
+		lunatask.StatusNext:       2,
+		lunatask.StatusWaiting:    3,
+		lunatask.StatusLater:      4,
+		lunatask.StatusCompleted:  5,
+	}
+
+	sort.Slice(tasks, func(i, j int) bool {
+		pI := 100
+		if tasks[i].Status != nil {
+			if p, ok := priority[*tasks[i].Status]; ok {
+				pI = p
+			}
+		}
+
+		pJ := 100
+		if tasks[j].Status != nil {
+			if p, ok := priority[*tasks[j].Status]; ok {
+				pJ = p
+			}
+		}
+
+		if pI != pJ {
+			return pI < pJ
+		}
+
+		// Secondary sort by creation date (newest first)
+		return tasks[i].CreatedAt.After(tasks[j].CreatedAt)
+	})
+}

+5 -1 cmd/note/update.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
diff --git a/cmd/note/update.go b/cmd/note/update.go
index f3ff9e3..866b14b 100644
--- a/cmd/note/update.go
+++ b/cmd/note/update.go
@@ -12,6 +12,7 @@ import (
 	"git.secluded.site/lune/internal/completion"
 	"git.secluded.site/lune/internal/config"
 	"git.secluded.site/lune/internal/dateutil"
+	"git.secluded.site/lune/internal/db"
 	"git.secluded.site/lune/internal/ui"
 	"git.secluded.site/lune/internal/validate"
 	"github.com/spf13/cobra"
@@ -39,7 +40,10 @@ func init() {
 }
 
 func runUpdate(cmd *cobra.Command, args []string) error {
-	id, err := validate.Reference(args[0])
+	idInput := args[0]
+	id := db.CompleteID(idInput)
+
+	id, err := validate.Reference(id)
 	if err != nil {
 		return err
 	}
+5 -1 cmd/task/update.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
diff --git a/cmd/task/update.go b/cmd/task/update.go
index f9c767d..9fe35c3 100644
--- a/cmd/task/update.go
+++ b/cmd/task/update.go
@@ -12,6 +12,7 @@ import (
 	"git.secluded.site/lune/internal/completion"
 	"git.secluded.site/lune/internal/config"
 	"git.secluded.site/lune/internal/dateutil"
+	"git.secluded.site/lune/internal/db"
 	"git.secluded.site/lune/internal/ui"
 	"git.secluded.site/lune/internal/validate"
 	"github.com/spf13/cobra"
@@ -52,7 +53,10 @@ func init() {
 }
 
 func runUpdate(cmd *cobra.Command, args []string) error {
-	id, err := validate.Reference(args[0])
+	idInput := args[0]
+	id := db.CompleteID(idInput)
+
+	id, err := validate.Reference(id)
 	if err != nil {
 		return err
 	}
+18 -0 internal/db/reader.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
diff --git a/internal/db/reader.go b/internal/db/reader.go
index 834e8b4..3499a36 100644
--- a/internal/db/reader.go
+++ b/internal/db/reader.go
@@ -147,6 +147,24 @@ func EnrichTask(taskID string) (string, error) {
 	return nameCache[taskID], nil
 }
 
+// CompleteID searches for the full UUID by an 8-character prefix.
+func CompleteID(prefix string) string {
+	if len(prefix) != 8 {
+		return prefix
+	}
+
+	if !cacheWarmed {
+		_ = WarmCache()
+	}
+
+	for id := range nameCache {
+		if strings.HasPrefix(id, prefix) {
+			return id
+		}
+	}
+	return prefix
+}
+
 // Area represents a Lunatask area found in the local database.
 type Area struct {
 	ID   string

+57 -0 cmd/task/interactive.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
diff --git a/cmd/task/interactive.go b/cmd/task/interactive.go
new file mode 100644
index 0000000..ba75d40
--- /dev/null
+++ b/cmd/task/interactive.go
@@ -0,0 +1,57 @@
+// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+package task
+
+import (
+	"git.secluded.site/go-lunatask"
+	"github.com/charmbracelet/huh"
+	"github.com/spf13/cobra"
+)
+
+func runUpdateInteractive(cmd *cobra.Command, task *lunatask.Task, builder *lunatask.TaskUpdateBuilder) error {
+	name := ""
+	if task.Name != nil {
+		name = *task.Name
+	}
+	if name == "" {
+		name = "Unnamed Task"
+	}
+
+	newStatus := ""
+	if task.Status != nil {
+		newStatus = string(*task.Status)
+	}
+	
+	form := huh.NewForm(
+		huh.NewGroup(
+			huh.NewInput().
+				Title("Task Name").
+				Value(&name),
+			huh.NewSelect[string]().
+				Title("Status").
+				Options(
+					huh.NewOption("Later", "later"),
+					huh.NewOption("Next", "next"),
+					huh.NewOption("In Progress", "in-progress"),
+					huh.NewOption("Waiting", "waiting"),
+					huh.NewOption("Completed", "completed"),
+				).
+				Value(&newStatus),
+		),
+	)
+	
+	if err := form.Run(); err != nil {
+		return err
+	}
+
+	builder.Name(name)
+	
+	if newStatus != "" {
+		status := lunatask.TaskStatus(newStatus)
+		builder.WithStatus(status)
+	}
+
+	return nil
+}
+29 -11 cmd/task/update.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
diff --git a/cmd/task/update.go b/cmd/task/update.go
index 9fe35c3..35403b6 100644
--- a/cmd/task/update.go
+++ b/cmd/task/update.go
@@ -16,6 +16,7 @@ import (
 	"git.secluded.site/lune/internal/ui"
 	"git.secluded.site/lune/internal/validate"
 	"github.com/spf13/cobra"
+	"github.com/spf13/pflag"
 )
 
 // UpdateCmd updates a task. Exported for potential use by shortcuts.
@@ -66,29 +67,46 @@ func runUpdate(cmd *cobra.Command, args []string) error {
 		return err
 	}
 
-	builder := apiClient.NewTaskUpdate(id)
-
-	if err := applyUpdateName(cmd, builder); err != nil {
-		return err
-	}
-
-	area, err := applyUpdateAreaAndGoal(cmd, builder)
+	task, err := apiClient.GetTask(cmd.Context(), id)
 	if err != nil {
 		return err
 	}
 
-	if err := applyUpdateFlags(cmd, builder, area); err != nil {
-		return err
+	builder := apiClient.NewTaskUpdate(id)
+
+	// Check if any flag is set
+	hasFlags := false
+	cmd.Flags().Visit(func(f *pflag.Flag) {
+		hasFlags = true
+	})
+
+	if !hasFlags {
+		if err := runUpdateInteractive(cmd, task, builder); err != nil {
+			return err
+		}
+	} else {
+		if err := applyUpdateName(cmd, builder); err != nil {
+			return err
+		}
+
+		area, err := applyUpdateAreaAndGoal(cmd, builder)
+		if err != nil {
+			return err
+		}
+
+		if err := applyUpdateFlags(cmd, builder, area); err != nil {
+			return err
+		}
 	}
 
-	task, err := ui.Spin("Updating task…", func() (*lunatask.Task, error) {
+	updatedTask, err := ui.Spin("Updating task…", func() (*lunatask.Task, error) {
 		return builder.Update(cmd.Context())
 	})
 	if err != nil {
 		return err
 	}
 
-	fmt.Fprintln(cmd.OutOrStdout(), ui.Success.Render("Updated task: "+task.ID))
+	fmt.Fprintln(cmd.OutOrStdout(), ui.Success.Render("Updated task: "+updatedTask.ID))
 
 	return nil
 }

+7 -1 cmd/task/interactive.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
diff --git a/cmd/task/interactive.go b/cmd/task/interactive.go
index ba75d40..a018873 100644
--- a/cmd/task/interactive.go
+++ b/cmd/task/interactive.go
@@ -6,15 +6,21 @@ package task
 
 import (
 	"git.secluded.site/go-lunatask"
+	"git.secluded.site/lune/internal/db"
 	"github.com/charmbracelet/huh"
 	"github.com/spf13/cobra"
 )
 
 func runUpdateInteractive(cmd *cobra.Command, task *lunatask.Task, builder *lunatask.TaskUpdateBuilder) error {
 	name := ""
-	if task.Name != nil {
+	// Try enriching from local DB first
+	dbName, _ := db.EnrichTask(task.ID)
+	if dbName != "" {
+		name = dbName
+	} else if task.Name != nil {
 		name = *task.Name
 	}
+	
 	if name == "" {
 		name = "Unnamed Task"
 	}

+75 -13 cmd/task/interactive.go link
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
diff --git a/cmd/task/interactive.go b/cmd/task/interactive.go
index a018873..b9380ed 100644
--- a/cmd/task/interactive.go
+++ b/cmd/task/interactive.go
@@ -3,8 +3,10 @@
 // SPDX-License-Identifier: AGPL-3.0-or-later
 
 package task
-
 import (
+	"fmt"
+	"strconv"
+
 	"git.secluded.site/go-lunatask"
 	"git.secluded.site/lune/internal/db"
 	"github.com/charmbracelet/huh"
@@ -12,15 +14,14 @@ import (
 )
 
 func runUpdateInteractive(cmd *cobra.Command, task *lunatask.Task, builder *lunatask.TaskUpdateBuilder) error {
+	// Existing fields
 	name := ""
-	// Try enriching from local DB first
 	dbName, _ := db.EnrichTask(task.ID)
 	if dbName != "" {
 		name = dbName
 	} else if task.Name != nil {
 		name = *task.Name
 	}
-	
 	if name == "" {
 		name = "Unnamed Task"
 	}
@@ -29,12 +30,35 @@ func runUpdateInteractive(cmd *cobra.Command, task *lunatask.Task, builder *luna
 	if task.Status != nil {
 		newStatus = string(*task.Status)
 	}
-	
+
+	// New fields
+	priority := "normal"
+	if task.Priority != nil {
+		priority = string(*task.Priority)
+	}
+
+	motivation := ""
+	if task.Motivation != nil {
+		motivation = string(*task.Motivation)
+	}
+
+	note := ""
+	if task.Note != nil {
+		note = *task.Note
+	}
+
+	estimate := 0
+	if task.Estimate != nil {
+		estimate = *task.Estimate
+	}
+	estStr := fmt.Sprintf("%d", estimate)
+
+	important := task.Eisenhower != nil && task.Eisenhower.IsImportant()
+	urgent := task.Eisenhower != nil && task.Eisenhower.IsUrgent()
+
 	form := huh.NewForm(
 		huh.NewGroup(
-			huh.NewInput().
-				Title("Task Name").
-				Value(&name),
+			huh.NewInput().Title("Task Name").Value(&name),
 			huh.NewSelect[string]().
 				Title("Status").
 				Options(
@@ -43,21 +67,59 @@ func runUpdateInteractive(cmd *cobra.Command, task *lunatask.Task, builder *luna
 					huh.NewOption("In Progress", "in-progress"),
 					huh.NewOption("Waiting", "waiting"),
 					huh.NewOption("Completed", "completed"),
-				).
-				Value(&newStatus),
+				).Value(&newStatus),
+		),
+		huh.NewGroup(
+			huh.NewSelect[string]().
+				Title("Priority").
+				Options(
+					huh.NewOption("Lowest", "lowest"),
+					huh.NewOption("Low", "low"),
+					huh.NewOption("Normal", "normal"),
+					huh.NewOption("High", "high"),
+					huh.NewOption("Highest", "highest"),
+				).Value(&priority),
+			huh.NewSelect[string]().
+				Title("Motivation").
+				Options(
+					huh.NewOption("Must", "must"),
+					huh.NewOption("Should", "should"),
+					huh.NewOption("Want", "want"),
+				).Value(&motivation),
+			huh.NewInput().Title("Note").Value(&note),
+			huh.NewInput().Title("Estimate (minutes)").Value(&estStr),
+		),
+		huh.NewGroup(
+			huh.NewConfirm().Title("Important?").Value(&important),
+			huh.NewConfirm().Title("Urgent?").Value(&urgent),
 		),
 	)
-	
+
 	if err := form.Run(); err != nil {
 		return err
 	}
 
+	// Apply updates
 	builder.Name(name)
-	
 	if newStatus != "" {
-		status := lunatask.TaskStatus(newStatus)
-		builder.WithStatus(status)
+		builder.WithStatus(lunatask.TaskStatus(newStatus))
+	}
+	if priority != "" {
+		p, _ := lunatask.ParsePriority(priority)
+		builder.Priority(p)
+	}
+	if motivation != "" {
+		m, _ := lunatask.ParseMotivation(motivation)
+		builder.WithMotivation(m)
 	}
+	builder.WithNote(note)
+	
+	if est, err := strconv.Atoi(estStr); err == nil {
+		builder.WithEstimate(est)
+	}
+	
+	if important { builder.Important() } else { builder.NotImportant() }
+	if urgent { builder.Urgent() } else { builder.NotUrgent() }
 
 	return nil
 }

+4 -1 cmd/task/update.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
diff --git a/cmd/task/update.go b/cmd/task/update.go
index 35403b6..f9c0b55 100644
--- a/cmd/task/update.go
+++ b/cmd/task/update.go
@@ -26,7 +26,10 @@ var UpdateCmd = &cobra.Command{
 	Long: `Update an existing task in Lunatask.
 
 Accepts a UUID or lunatask:// deep link.
-Only specified flags are modified; other fields remain unchanged.`,
+Only specified flags are modified; other fields remain unchanged.
+
+If no flags are provided, lune opens an interactive TUI menu to guide 
+you through updating all available fields.`,
 	Args: cobra.ExactArgs(1),
 	RunE: runUpdate,
 }

+9 -4 cmd/note/delete.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
diff --git a/cmd/note/delete.go b/cmd/note/delete.go
index 6f22b0e..d6fa119 100644
--- a/cmd/note/delete.go
+++ b/cmd/note/delete.go
@@ -9,6 +9,7 @@ import (
 
 	"git.secluded.site/go-lunatask"
 	"git.secluded.site/lune/internal/client"
+	"git.secluded.site/lune/internal/db"
 	"git.secluded.site/lune/internal/ui"
 	"git.secluded.site/lune/internal/validate"
 	"github.com/spf13/cobra"
@@ -20,9 +21,10 @@ var DeleteCmd = &cobra.Command{
 	Short: "Delete a note",
 	Long: `Delete a note from Lunatask.
 
-Accepts a UUID or lunatask:// deep link.`,
-	Args: cobra.ExactArgs(1),
-	RunE: runDelete,
+Accepts a UUID, lunatask:// deep link, or 8-char short ID.`,
+	Args:         cobra.ExactArgs(1),
+	RunE:         runDelete,
+	SilenceUsage: true,
 }
 
 func init() {
@@ -30,7 +32,10 @@ func init() {
 }
 
 func runDelete(cmd *cobra.Command, args []string) error {
-	id, err := validate.Reference(args[0])
+	idInput := args[0]
+	id := db.CompleteID(idInput)
+
+	id, err := validate.Reference(id)
 	if err != nil {
 		return err
 	}
+36 -8 cmd/note/list.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
diff --git a/cmd/note/list.go b/cmd/note/list.go
index dcdad56..cb1ef6b 100644
--- a/cmd/note/list.go
+++ b/cmd/note/list.go
@@ -178,12 +178,18 @@ func outputTableEnriched(cmd *cobra.Command, notes []enrichedNote) error {
 	rows := make([][]string, 0, len(notes))
 
 	for _, note := range notes {
-		notebook := "-"
+		// Resolve Area name
+		notebookName := "-"
 		if note.NotebookID != nil {
-			notebook = *note.NotebookID
+			notebookName = *note.NotebookID
 			if cfg != nil {
 				if nb := cfg.NotebookByID(*note.NotebookID); nb != nil {
-					notebook = nb.Key
+					notebookName = nb.Key
+				} else if cfg.Experimental.LocalDB {
+					// Proactive area lookup in local DB
+					if dbName, _ := db.EnrichTask(*note.NotebookID); dbName != "" {
+						notebookName = dbName
+					}
 				}
 			}
 		}
@@ -202,21 +208,43 @@ func outputTableEnriched(cmd *cobra.Command, notes []enrichedNote) error {
 		if name == "" {
 			name = note.ID[:8] + "..."
 		}
+		
+		// Truncate notebook name for better table layout
+		if len(notebookName) > 8 {
+			notebookName = notebookName[:8] + "..."
+		}
 
-		rows = append(rows, []string{name, notebook, dateOn, pinned})
+		rows = append(rows, []string{note.ID[:8], name, notebookName, dateOn, pinned})
 	}
 
 	tbl := table.New().
-		Headers("NAME", "NOTEBOOK", "DATE", "📌").
+		Headers("ID", "NAME", "NOTEBOOK", "DATE", "📌").
 		Rows(rows...).
 		StyleFunc(func(row, col int) lipgloss.Style {
+			style := lipgloss.NewStyle()
+			
+			// Set column widths via style
+			switch col {
+			case 0: // ID
+				style = style.Width(10)
+			case 1: // NAME
+				style = style.Width(35)
+			case 2: // NOTEBOOK
+				style = style.Width(15)
+			case 3: // DATE
+				style = style.Width(12)
+			case 4: // PIN
+				style = style.Width(5)
+			}
+
 			if row == table.HeaderRow {
-				return ui.TableHeaderStyle()
+				return style.Inherit(ui.TableHeaderStyle())
 			}
 
-			return lipgloss.NewStyle()
+			return style
 		}).
-		Border(ui.TableBorder())
+		Border(ui.TableBorder()).
+		BorderRow(true)
 
 	fmt.Fprintln(cmd.OutOrStdout(), tbl.Render())
 
+9 -4 cmd/note/show.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
diff --git a/cmd/note/show.go b/cmd/note/show.go
index 438ed18..ceaf103 100644
--- a/cmd/note/show.go
+++ b/cmd/note/show.go
@@ -11,6 +11,7 @@ import (
 	"git.secluded.site/go-lunatask"
 	"git.secluded.site/lune/internal/client"
 	"git.secluded.site/lune/internal/config"
+	"git.secluded.site/lune/internal/db"
 	"git.secluded.site/lune/internal/ui"
 	"git.secluded.site/lune/internal/validate"
 	"github.com/spf13/cobra"
@@ -22,12 +23,13 @@ var ShowCmd = &cobra.Command{
 	Short: "Show note details",
 	Long: `Show detailed information for a note.
 
-Accepts a UUID or lunatask:// deep link.
+Accepts a UUID, lunatask:// deep link, or 8-char short ID.
 
 Note: Due to end-to-end encryption, note name and content
 are not available through the API. Only metadata is shown.`,
-	Args: cobra.ExactArgs(1),
-	RunE: runShow,
+	Args:         cobra.ExactArgs(1),
+	RunE:         runShow,
+	SilenceUsage: true,
 }
 
 func init() {
@@ -35,7 +37,10 @@ func init() {
 }
 
 func runShow(cmd *cobra.Command, args []string) error {
-	id, err := validate.Reference(args[0])
+	idInput := args[0]
+	id := db.CompleteID(idInput)
+
+	id, err := validate.Reference(id)
 	if err != nil {
 		return err
 	}

+43 -0 cmd/note/interactive.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
diff --git a/cmd/note/interactive.go b/cmd/note/interactive.go
new file mode 100644
index 0000000..9e07cc7
--- /dev/null
+++ b/cmd/note/interactive.go
@@ -0,0 +1,43 @@
+// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+package note
+
+import (
+	"git.secluded.site/go-lunatask"
+	"git.secluded.site/lune/internal/db"
+	"github.com/charmbracelet/huh"
+	"github.com/spf13/cobra"
+)
+
+func runUpdateInteractive(cmd *cobra.Command, note *lunatask.Note, builder *lunatask.NoteUpdateBuilder) error {
+	name := ""
+	dbName, _ := db.EnrichTask(note.ID)
+	if dbName != "" {
+		name = dbName
+	}
+	if name == "" {
+		name = "Unnamed Note"
+	}
+
+	dateStr := ""
+	if note.DateOn != nil {
+		dateStr = note.DateOn.Time.Format("2006-01-02")
+	}
+
+	form := huh.NewForm(
+		huh.NewGroup(
+			huh.NewInput().Title("Note Name").Value(&name),
+			huh.NewInput().Title("Date (YYYY-MM-DD)").Value(&dateStr),
+		),
+	)
+	
+	if err := form.Run(); err != nil {
+		return err
+	}
+
+	builder.WithName(name)
+	
+	return nil
+}
+30 -15 cmd/note/update.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
diff --git a/cmd/note/update.go b/cmd/note/update.go
index 866b14b..a031ec2 100644
--- a/cmd/note/update.go
+++ b/cmd/note/update.go
@@ -16,6 +16,7 @@ import (
 	"git.secluded.site/lune/internal/ui"
 	"git.secluded.site/lune/internal/validate"
 	"github.com/spf13/cobra"
+	"github.com/spf13/pflag"
 )
 
 // UpdateCmd updates a note. Exported for potential use by shortcuts.
@@ -53,32 +54,46 @@ func runUpdate(cmd *cobra.Command, args []string) error {
 		return err
 	}
 
-	builder := apiClient.NewNoteUpdate(id)
-
-	if err := applyUpdateName(cmd, builder); err != nil {
-		return err
-	}
-
-	if err := applyUpdateNotebook(cmd, builder); err != nil {
+	note, err := apiClient.GetNote(cmd.Context(), id)
+	if err != nil {
 		return err
 	}
 
-	if err := applyUpdateContent(cmd, builder); err != nil {
-		return err
-	}
+	builder := apiClient.NewNoteUpdate(id)
 
-	if err := applyUpdateDate(cmd, builder); err != nil {
-		return err
-	}
+	// Check if any flag is set
+	hasFlags := false
+	cmd.Flags().Visit(func(f *pflag.Flag) {
+		hasFlags = true
+	})
 
-	note, err := ui.Spin("Updating note…", func() (*lunatask.Note, error) {
+	if !hasFlags {
+		if err := runUpdateInteractive(cmd, note, builder); err != nil {
+			return err
+		}
+	} else {
+		if err := applyUpdateName(cmd, builder); err != nil {
+			return err
+		}
+		if err := applyUpdateNotebook(cmd, builder); err != nil {
+			return err
+		}
+		if err := applyUpdateContent(cmd, builder); err != nil {
+			return err
+		}
+		if err := applyUpdateDate(cmd, builder); err != nil {
+			return err
+		}
+	}
+
+	updatedNote, err := ui.Spin("Updating note…", func() (*lunatask.Note, error) {
 		return builder.Update(cmd.Context())
 	})
 	if err != nil {
 		return err
 	}
 
-	fmt.Fprintln(cmd.OutOrStdout(), ui.Success.Render("Updated note: "+note.ID))
+	fmt.Fprintln(cmd.OutOrStdout(), ui.Success.Render("Updated note: "+updatedNote.ID))
 
 	return nil
 }

+10 -0 cmd/note/interactive.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
diff --git a/cmd/note/interactive.go b/cmd/note/interactive.go
index 9e07cc7..463c8c1 100644
--- a/cmd/note/interactive.go
+++ b/cmd/note/interactive.go
@@ -26,10 +26,16 @@ func runUpdateInteractive(cmd *cobra.Command, note *lunatask.Note, builder *luna
 		dateStr = note.DateOn.Time.Format("2006-01-02")
 	}
 
+	notebookID := ""
+	if note.NotebookID != nil {
+		notebookID = *note.NotebookID
+	}
+
 	form := huh.NewForm(
 		huh.NewGroup(
 			huh.NewInput().Title("Note Name").Value(&name),
 			huh.NewInput().Title("Date (YYYY-MM-DD)").Value(&dateStr),
+			huh.NewInput().Title("Notebook ID").Value(&notebookID),
 		),
 	)
 	
@@ -38,6 +44,10 @@ func runUpdateInteractive(cmd *cobra.Command, note *lunatask.Note, builder *luna
 	}
 
 	builder.WithName(name)
+	if notebookID != "" {
+		resolvedNotebookID := db.CompleteID(notebookID)
+		builder.InNotebook(resolvedNotebookID)
+	}
 	
 	return nil
 }
+15 -7 cmd/note/list.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
diff --git a/cmd/note/list.go b/cmd/note/list.go
index cb1ef6b..feec25e 100644
--- a/cmd/note/list.go
+++ b/cmd/note/list.go
@@ -214,27 +214,34 @@ func outputTableEnriched(cmd *cobra.Command, notes []enrichedNote) error {
 			notebookName = notebookName[:8] + "..."
 		}
 
-		rows = append(rows, []string{note.ID[:8], name, notebookName, dateOn, pinned})
-	}
+		notebookIDShort := ""
+		if note.NotebookID != nil {
+			notebookIDShort = (*note.NotebookID)[:8]
+		}
 
-	tbl := table.New().
-		Headers("ID", "NAME", "NOTEBOOK", "DATE", "📌").
+		rows = append(rows, []string{note.ID[:8], name, notebookName, dateOn, pinned, notebookIDShort})
+		}
+
+		tbl := table.New().
+		Headers("ID", "NAME", "NOTEBOOK", "DATE", "📌", "NB-ID").
 		Rows(rows...).
 		StyleFunc(func(row, col int) lipgloss.Style {
 			style := lipgloss.NewStyle()
-			
+
 			// Set column widths via style
 			switch col {
 			case 0: // ID
 				style = style.Width(10)
 			case 1: // NAME
-				style = style.Width(35)
+				style = style.Width(30)
 			case 2: // NOTEBOOK
 				style = style.Width(15)
 			case 3: // DATE
 				style = style.Width(12)
 			case 4: // PIN
 				style = style.Width(5)
+			case 5: // NB-ID
+				style = style.Width(10)
 			}
 
 			if row == table.HeaderRow {
@@ -243,7 +250,8 @@ func outputTableEnriched(cmd *cobra.Command, notes []enrichedNote) error {
 
 			return style
 		}).
-		Border(ui.TableBorder()).
+		Border(ui.TableBorder())
+.
 		BorderRow(true)
 
 	fmt.Fprintln(cmd.OutOrStdout(), tbl.Render())
+7 -2 cmd/note/update.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
diff --git a/cmd/note/update.go b/cmd/note/update.go
index a031ec2..9131022 100644
--- a/cmd/note/update.go
+++ b/cmd/note/update.go
@@ -120,14 +120,19 @@ func applyUpdateNotebook(cmd *cobra.Command, builder *lunatask.NoteUpdateBuilder
 		return nil
 	}
 
+	// Try resolving notebook via complete ID first
+	notebookID := db.CompleteID(notebookKey)
+
 	cfg, err := config.Load()
 	if err != nil {
 		return err
 	}
 
-	notebook := cfg.NotebookByKey(notebookKey)
+	notebook := cfg.NotebookByKey(notebookID)
 	if notebook == nil {
-		return fmt.Errorf("%w: %s", ErrUnknownNotebook, notebookKey)
+		// Fallback to ID directly if not found by key
+		builder.InNotebook(notebookID)
+		return nil
 	}
 
 	builder.InNotebook(notebook.ID)

+1 -2 cmd/note/list.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
diff --git a/cmd/note/list.go b/cmd/note/list.go
index feec25e..71de92f 100644
--- a/cmd/note/list.go
+++ b/cmd/note/list.go
@@ -250,8 +250,7 @@ func outputTableEnriched(cmd *cobra.Command, notes []enrichedNote) error {
 
 			return style
 		}).
-		Border(ui.TableBorder())
-.
+		Border(ui.TableBorder()).
 		BorderRow(true)
 
 	fmt.Fprintln(cmd.OutOrStdout(), tbl.Render())

+1 -0 cmd/note/note.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
diff --git a/cmd/note/note.go b/cmd/note/note.go
index 572cfb9..7861e13 100644
--- a/cmd/note/note.go
+++ b/cmd/note/note.go
@@ -17,6 +17,7 @@ var Cmd = &cobra.Command{
 func init() {
 	Cmd.AddCommand(AddCmd)
 	Cmd.AddCommand(ListCmd)
+	Cmd.AddCommand(NotebookListCmd)
 	Cmd.AddCommand(ShowCmd)
 	Cmd.AddCommand(UpdateCmd)
 	Cmd.AddCommand(DeleteCmd)
+89 -0 cmd/note/notebook_list.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
diff --git a/cmd/note/notebook_list.go b/cmd/note/notebook_list.go
new file mode 100644
index 0000000..59cf657
--- /dev/null
+++ b/cmd/note/notebook_list.go
@@ -0,0 +1,89 @@
+// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+package note
+
+import (
+	"fmt"
+
+	"git.secluded.site/go-lunatask"
+	"git.secluded.site/lune/internal/client"
+	"git.secluded.site/lune/internal/config"
+	"git.secluded.site/lune/internal/db"
+	"git.secluded.site/lune/internal/ui"
+	"github.com/charmbracelet/lipgloss"
+	"github.com/charmbracelet/lipgloss/table"
+	"github.com/spf13/cobra"
+)
+
+// NotebookListCmd lists notebooks extracted from notes.
+var NotebookListCmd = &cobra.Command{
+	Use:   "notebook-list",
+	Short: "List notebooks found in notes",
+	Long:  `List all notebooks identified in your notes with their names and IDs.`,
+	RunE:  runNotebookList,
+}
+
+func runNotebookList(cmd *cobra.Command, _ []string) error {
+	apiClient, err := client.New()
+	if err != nil {
+		return err
+	}
+
+	notes, err := ui.Spin("Fetching notes…", func() ([]lunatask.Note, error) {
+		return apiClient.ListNotes(cmd.Context(), nil)
+	})
+	if err != nil {
+		return err
+	}
+
+	cfg, _ := config.Load()
+	
+	// Map to store unique notebooks: ID -> Name
+	notebooks := make(map[string]string)
+	
+	for _, n := range notes {
+		if n.NotebookID != nil {
+			id := *n.NotebookID
+			if _, exists := notebooks[id]; !exists {
+				// Determine name
+				name := id
+				if cfg != nil {
+					if nb := cfg.NotebookByID(id); nb != nil {
+						name = nb.Name
+					} else if cfg.Experimental.LocalDB {
+						if dbName, _ := db.EnrichTask(id); dbName != "" {
+							name = dbName
+						}
+					}
+				}
+				notebooks[id] = name
+			}
+		}
+	}
+
+	if len(notebooks) == 0 {
+		fmt.Fprintln(cmd.OutOrStdout(), "No notebooks found")
+		return nil
+	}
+
+	rows := make([][]string, 0)
+	for id, name := range notebooks {
+		rows = append(rows, []string{id[:8], name})
+	}
+
+	tbl := table.New().
+		Headers("ID", "NAME").
+		Rows(rows...).
+		StyleFunc(func(row, col int) lipgloss.Style {
+			if row == table.HeaderRow {
+				return ui.TableHeaderStyle()
+			}
+			return lipgloss.NewStyle()
+		}).
+		Border(ui.TableBorder())
+
+	fmt.Fprintln(cmd.OutOrStdout(), tbl.Render())
+	return nil
+}

+1 -0 cmd/note/note.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
diff --git a/cmd/note/note.go b/cmd/note/note.go
index 7861e13..0440a8c 100644
--- a/cmd/note/note.go
+++ b/cmd/note/note.go
@@ -19,6 +19,7 @@ func init() {
 	Cmd.AddCommand(ListCmd)
 	Cmd.AddCommand(NotebookListCmd)
 	Cmd.AddCommand(ShowCmd)
+	Cmd.AddCommand(ShowContentCmd)
 	Cmd.AddCommand(UpdateCmd)
 	Cmd.AddCommand(DeleteCmd)
 }
+37 -0 cmd/note/show_content.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
diff --git a/cmd/note/show_content.go b/cmd/note/show_content.go
new file mode 100644
index 0000000..980faee
--- /dev/null
+++ b/cmd/note/show_content.go
@@ -0,0 +1,37 @@
+// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+package note
+
+import (
+	"fmt"
+
+	"git.secluded.site/lune/internal/db"
+	"github.com/spf13/cobra"
+)
+
+// ShowContentCmd displays a note's content by ID.
+var ShowContentCmd = &cobra.Command{
+	Use:   "show-content ID",
+	Short: "Show note content",
+	Long: `Retrieve and print the content of a note from your local database.
+
+Requires master password configuration and local DB support enabled.`,
+	Args:         cobra.ExactArgs(1),
+	RunE:         runShowContent,
+	SilenceUsage: true,
+}
+
+func runShowContent(cmd *cobra.Command, args []string) error {
+	idInput := args[0]
+	id := db.CompleteID(idInput)
+
+	content, err := db.GetNoteContent(id)
+	if err != nil {
+		return err
+	}
+
+	fmt.Fprintln(cmd.OutOrStdout(), content)
+	return nil
+}
+36 -0 internal/db/reader.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
diff --git a/internal/db/reader.go b/internal/db/reader.go
index 3499a36..f5917c8 100644
--- a/internal/db/reader.go
+++ b/internal/db/reader.go
@@ -8,6 +8,7 @@ import (
 	"bytes"
 	"encoding/binary"
 	"encoding/json"
+	"fmt"
 	"os"
 	"path/filepath"
 	"regexp"
@@ -147,6 +148,41 @@ func EnrichTask(taskID string) (string, error) {
 	return nameCache[taskID], nil
 }
 
+// GetNoteContent retrieves the content of a note from the local database.
+func GetNoteContent(noteID string) (string, error) {
+	dbPath := GetDefaultPath()
+	if _, err := os.Stat(dbPath); os.IsNotExist(err) {
+		return "", fmt.Errorf("local database not found")
+	}
+
+	o := &opt.Options{
+		Comparer: idbComparer{},
+		ReadOnly: true,
+	}
+
+	db, err := leveldb.OpenFile(dbPath, o)
+	if err != nil {
+		return "", err
+	}
+	defer db.Close()
+
+	iter := db.NewIterator(nil, nil)
+	defer iter.Release()
+
+	for iter.Next() {
+		val := iter.Value()
+		if bytes.Contains(val, []byte(noteID)) {
+			// Extract content using regex
+			re := regexp.MustCompile(`"content"\s*:\s*"([^"]+)"`)
+			matches := re.FindSubmatch(val)
+			if len(matches) > 1 {
+				return cleanName(string(matches[1])), nil
+			}
+		}
+	}
+	return "", fmt.Errorf("content not found for note ID: %s", noteID)
+}
+
 // CompleteID searches for the full UUID by an 8-character prefix.
 func CompleteID(prefix string) string {
 	if len(prefix) != 8 {

+6 -1 cmd/note/show_content.go link
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
diff --git a/cmd/note/show_content.go b/cmd/note/show_content.go
index 980faee..8568877 100644
--- a/cmd/note/show_content.go
+++ b/cmd/note/show_content.go
@@ -6,6 +6,7 @@ package note
 
 import (
 	"fmt"
+	"strings"
 
 	"git.secluded.site/lune/internal/db"
 	"github.com/spf13/cobra"
@@ -32,6 +33,10 @@ func runShowContent(cmd *cobra.Command, args []string) error {
 		return err
 	}
 
-	fmt.Fprintln(cmd.OutOrStdout(), content)
+	// Replace literal '\n' sequences with real newlines, and literal '\\' with spaces
+	formattedContent := strings.ReplaceAll(content, "\\n", "\n")
+	formattedContent = strings.ReplaceAll(formattedContent, "\\\\", " ")
+
+	fmt.Fprintln(cmd.OutOrStdout(), formattedContent)
 	return nil
 }