From 3427b25e3dfeddd79b6cb2429c3a1e292be1dd1b Mon Sep 17 00:00:00 2001 From: Nate Meyer <672246+notnmeyer@users.noreply.github.com> Date: Wed, 15 Apr 2026 19:01:18 -0700 Subject: [PATCH 1/3] move todos --- internal/daylog/daylog.go | 35 +++++++++++++++++++-- internal/daylog/daylog_test.go | 56 ++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 3 deletions(-) diff --git a/internal/daylog/daylog.go b/internal/daylog/daylog.go index 6ec9540..181b743 100644 --- a/internal/daylog/daylog.go +++ b/internal/daylog/daylog.go @@ -141,17 +141,46 @@ func createIfMissing(d *DayLog) error { return err } - file, err := os.Create(d.Path) + f, err := os.Create(d.Path) if err != nil { return err } - defer file.Close() + defer f.Close() year, month, day := d.Date.Year(), int(d.Date.Month()), d.Date.Day() header := fmt.Sprintf("# %d/%02d/%02d\n\n", year, month, day) - if _, err := file.WriteString(header); err != nil { + if _, err := f.WriteString(header); err != nil { return err } + if todos := carryOverTodos(d.ProjectPath, *d.Date); len(todos) > 0 { + _, err = f.WriteString(strings.Join(todos, "\n") + "\n") + if err != nil { + return err + } + } + return nil } + +// carryOverTodos reads the previous log and returns any lines containing "TODO". +func carryOverTodos(projectPath string, now time.Time) []string { + prev, err := file.PreviousLog(projectPath, file.NewLogProvider(), now) + if err != nil { + return nil + } + + prevPath := filepath.Join(projectPath, prev, "log.md") + content, err := os.ReadFile(prevPath) + if err != nil { + return nil + } + + var todos []string + for _, line := range strings.Split(string(content), "\n") { + if strings.Contains(line, "TODO") { + todos = append(todos, line) + } + } + return todos +} diff --git a/internal/daylog/daylog_test.go b/internal/daylog/daylog_test.go index e8f421a..b859e7f 100644 --- a/internal/daylog/daylog_test.go +++ b/internal/daylog/daylog_test.go @@ -9,6 +9,19 @@ import ( "time" ) +// writePrevLog creates a log file for the given date under projectPath. +func writePrevLog(t *testing.T, projectPath, dateDir, content string) { + t.Helper() + dir := filepath.Join(projectPath, dateDir) + if err := os.MkdirAll(dir, 0755); err != nil { + t.Fatalf("creating prev log dir: %v", err) + } + path := filepath.Join(dir, "log.md") + if err := os.WriteFile(path, []byte(content), 0644); err != nil { + t.Fatalf("writing prev log: %v", err) + } +} + func testDayLog(t *testing.T) *DayLog { t.Helper() dir := t.TempDir() @@ -90,6 +103,49 @@ func TestAppend(t *testing.T) { } } +func TestCarryOverTodos(t *testing.T) { + tests := []struct { + name string + prevContent string + expectedFile string + }{ + { + name: "no todos in previous log", + prevContent: "# 2025/12/01\n\ndid some work\n", + expectedFile: "# 2025/12/02\n\n", + }, + { + name: "todos are copied to new log", + prevContent: "# 2025/12/01\n\n- TODO: write tests\n- TODO: fix bug\n- done thing\n", + expectedFile: "# 2025/12/02\n\n- TODO: write tests\n- TODO: fix bug\n", + }, + { + name: "no previous log", + prevContent: "", + expectedFile: "# 2025/12/02\n\n", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + dl := testDayLog(t) + + if tt.prevContent != "" { + writePrevLog(t, dl.ProjectPath, "2025/12/01", tt.prevContent) + } + + if err := createIfMissing(dl); err != nil { + t.Fatalf("createIfMissing() error = %v", err) + } + + got := readFile(t, dl.Path) + if got != tt.expectedFile { + t.Errorf("file contents =\n%q\nwant\n%q", got, tt.expectedFile) + } + }) + } +} + func strPtr(s string) *string { return &s } func TestProjectPathAt(t *testing.T) { From 9246056f72b31cb9deb9638432dcfc89cdbb281d Mon Sep 17 00:00:00 2001 From: Nate Meyer <672246+notnmeyer@users.noreply.github.com> Date: Wed, 15 Apr 2026 19:33:28 -0700 Subject: [PATCH 2/3] create log for "show" command so TODOs are moved --- internal/daylog/daylog.go | 10 +++++++--- internal/file/file.go | 20 +++++++++----------- internal/file/file_test.go | 8 ++++---- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/internal/daylog/daylog.go b/internal/daylog/daylog.go index 181b743..b42ee65 100644 --- a/internal/daylog/daylog.go +++ b/internal/daylog/daylog.go @@ -104,6 +104,10 @@ func (d *DayLog) Edit() error { } func (d *DayLog) Show(format string) (string, error) { + if err := createIfMissing(d); err != nil { + return "", err + } + contents, err := editor.Read(d.Path) if err != nil { return "", err @@ -163,9 +167,9 @@ func createIfMissing(d *DayLog) error { return nil } -// carryOverTodos reads the previous log and returns any lines containing "TODO". -func carryOverTodos(projectPath string, now time.Time) []string { - prev, err := file.PreviousLog(projectPath, file.NewLogProvider(), now) +// carryOverTodos reads the log before `before` and returns any lines containing "TODO". +func carryOverTodos(projectPath string, before time.Time) []string { + prev, err := file.PreviousLog(projectPath, file.NewLogProvider(), before) if err != nil { return nil } diff --git a/internal/file/file.go b/internal/file/file.go index a73c8e1..3d4ce12 100644 --- a/internal/file/file.go +++ b/internal/file/file.go @@ -68,15 +68,15 @@ func (LogProvider) GetLogs(projectPath string) ([]string, error) { return validFiles, nil } -func PreviousLog(path string, provider FileLogProvider, now time.Time) (string, error) { +func PreviousLog(path string, provider FileLogProvider, before time.Time) (string, error) { // get all the logs logs, err := provider.GetLogs(path) if err != nil { return "", err } - // find the most recent existing log before today - prev, exists := findPreviousLog(logs, now) + // find the most recent existing log before the given date + prev, exists := findPreviousLog(logs, before) if !exists { return "", fmt.Errorf("no previous log found") } @@ -102,15 +102,13 @@ func convertLogToDisplayName(log string) string { return path.Join(split[0], split[1], split[2]) } -// logs is the list of keys of all logs. each log is YYYY/MM/DD, which the most recent date at index 0 -func findPreviousLog(logs []string, now time.Time) (string, bool) { - today := now.Format("2006/01/02") - +// logs is the list of keys of all logs. each log is YYYY/MM/DD, with the most recent date at index 0 +func findPreviousLog(logs []string, before time.Time) (string, bool) { + // Compare dates only so time-of-day doesn't affect the result + beforeDate := time.Date(before.Year(), before.Month(), before.Day(), 0, 0, 0, 0, time.UTC) for _, log := range logs { - if _, err := time.Parse("2006/01/02", log); err != nil { - continue - } - if log < today { + l, err := time.Parse("2006/01/02", log) + if err == nil && l.Before(beforeDate) { return log, true } } diff --git a/internal/file/file_test.go b/internal/file/file_test.go index b48a7c4..2fc4cef 100644 --- a/internal/file/file_test.go +++ b/internal/file/file_test.go @@ -114,7 +114,7 @@ func TestConvertLogToDisplayName(t *testing.T) { } func TestPreviousLog(t *testing.T) { - now := time.Date(2025, 12, 2, 0, 0, 0, 0, time.UTC) + before := time.Date(2025, 12, 2, 0, 0, 0, 0, time.UTC) tests := []struct { name string @@ -147,7 +147,7 @@ func TestPreviousLog(t *testing.T) { logs: tt.logs, } t.Run(tt.name, func(t *testing.T) { - prev, err := PreviousLog("mock/path", provider, now) + prev, err := PreviousLog("mock/path", provider, before) if err != nil && err.Error() != tt.err.Error() { t.Errorf("findPreviousLog(%v) = found: %v, want: %v", tt.logs, err, tt.err) } @@ -172,7 +172,7 @@ func TestPreviousLog_PropagatesProviderError(t *testing.T) { } func TestFindPreviousLog(t *testing.T) { - now := time.Date(2025, 12, 2, 0, 0, 0, 0, time.UTC) + before := time.Date(2025, 12, 2, 0, 0, 0, 0, time.UTC) tests := []struct { name string @@ -214,7 +214,7 @@ func TestFindPreviousLog(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - prev, found := findPreviousLog(tt.logs, now) + prev, found := findPreviousLog(tt.logs, before) if found != tt.found { t.Errorf("findPreviousLog(%v) = found: %v, want: %v", tt.logs, found, tt.found) } From 03fb657c6979764b5cf3e7f05e4a72f03050cc0b Mon Sep 17 00:00:00 2001 From: Nate Meyer <672246+notnmeyer@users.noreply.github.com> Date: Fri, 26 Jun 2026 17:34:54 -0700 Subject: [PATCH 3/3] it matches "- TODO:" --- internal/daylog/daylog.go | 4 ++-- internal/daylog/daylog_test.go | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/internal/daylog/daylog.go b/internal/daylog/daylog.go index b42ee65..313afbf 100644 --- a/internal/daylog/daylog.go +++ b/internal/daylog/daylog.go @@ -167,7 +167,7 @@ func createIfMissing(d *DayLog) error { return nil } -// carryOverTodos reads the log before `before` and returns any lines containing "TODO". +// carryOverTodos reads the log before `before` and returns any lines starting with "- TODO:". func carryOverTodos(projectPath string, before time.Time) []string { prev, err := file.PreviousLog(projectPath, file.NewLogProvider(), before) if err != nil { @@ -182,7 +182,7 @@ func carryOverTodos(projectPath string, before time.Time) []string { var todos []string for _, line := range strings.Split(string(content), "\n") { - if strings.Contains(line, "TODO") { + if strings.HasPrefix(line, "- TODO:") { todos = append(todos, line) } } diff --git a/internal/daylog/daylog_test.go b/internal/daylog/daylog_test.go index b859e7f..722b410 100644 --- a/internal/daylog/daylog_test.go +++ b/internal/daylog/daylog_test.go @@ -119,6 +119,11 @@ func TestCarryOverTodos(t *testing.T) { prevContent: "# 2025/12/01\n\n- TODO: write tests\n- TODO: fix bug\n- done thing\n", expectedFile: "# 2025/12/02\n\n- TODO: write tests\n- TODO: fix bug\n", }, + { + name: "only lines starting with - TODO: are copied", + prevContent: "# 2025/12/01\n\n- TODO: write tests\nthinking about TODOs\n - TODO: indented note\n- did a TODO\n", + expectedFile: "# 2025/12/02\n\n- TODO: write tests\n", + }, { name: "no previous log", prevContent: "",