git/dew/main

docs: add README, import issues

diff --git a/.gitignore b/.gitignore
index 8b23f6b..c316b24 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,8 @@
*.exe
*.log
/out/
+main
+*.bkp
+*~
+.DS_Store
+/target/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..519f452
--- /dev/null
+++ b/README.md
@@ -0,0 +1,20 @@
+# dew
+
+A task manager for the terminal.
+
+Tasks have a due date and an optional recurrence; recurring tasks roll forward when
+completed. There is a TUI for working through them, and a CLI for the times when
+opening the TUI is more than the job needs.
+
+## Building
+
+ go build -o dew ./cmd/dew
+ ./dew
+
+## Layout
+
+ cmd/dew/ entry point and argument handling
+ internal/dew/ tasks, storage, CLI and TUI
+ tests/ CLI-level tests
+
+Tasks are kept in a file under your home directory, alongside a small config file.
diff --git a/issues/0001-wrap-errors-with-w-instead-of-v.md b/issues/0001-wrap-errors-with-w-instead-of-v.md
new file mode 100644
index 0000000..0fda2a5
--- /dev/null
+++ b/issues/0001-wrap-errors-with-w-instead-of-v.md
@@ -0,0 +1,10 @@
+---
+title: wrap errors with %w instead of %v
+status: open
+labels: [good-first-issue, refactor]
+created: 2026-09-18
+closed:
+---
+Every `fmt.Errorf` in the codebase formats the wrapped error with `%v`, so nothing is unwrappable and callers can't use `errors.Is`/`errors.As`. A mechanical pass to `%w` would make the error chains actually useful.
+
+Files: `actions.go`, `config.go`, `main.go`, `task.go`, `tui.go`. Good place to start if you're new to the code.
diff --git a/issues/0002-server-backend-is-a-stub.md b/issues/0002-server-backend-is-a-stub.md
new file mode 100644
index 0000000..34901d5
--- /dev/null
+++ b/issues/0002-server-backend-is-a-stub.md
@@ -0,0 +1,10 @@
+---
+title: server backend is a stub
+status: open
+labels: [backburner, enhancement]
+created: 2026-09-18
+closed:
+---
+`backend_mode`, `api_url`, and `api_token` all exist in the config, and selecting `backend_mode: "server"` renders a "server mode coming soon!" cat screen, but there is no API integration behind it at all.
+
+Long-term this is the plan — dew eventually talks to `https://todo.myn.sh/api` — but it is explicitly on the backburner for now. Filing it so the dead config keys and the placeholder screen don't read as accidental in the meantime. Either implement it or drop the keys until it's real.
diff --git a/issues/0003-add-a-makefile-or-justfile-with-a-radicle-publish.md b/issues/0003-add-a-makefile-or-justfile-with-a-radicle-publish.md
new file mode 100644
index 0000000..77496eb
--- /dev/null
+++ b/issues/0003-add-a-makefile-or-justfile-with-a-radicle-publish.md
@@ -0,0 +1,10 @@
+---
+title: add a Makefile or justfile with a radicle publish recipe
+status: open
+labels: [enhancement]
+created: 2026-09-18
+closed:
+---
+No build tooling in the repo. Add targets for `build`, `run`, `fmt`, `vet`, `test`, and a combined `check`, plus a publish recipe for Radicle (`rad sync --announce`, and maybe a tag-and-broadcast target like grrs's `publish-tag`).
+
+Pick Makefile (like intake) or justfile (like grrs) — whichever we want to standardize on.
diff --git a/issues/0004-add-a-readme.md b/issues/0004-add-a-readme.md
new file mode 100644
index 0000000..d62e1d9
--- /dev/null
+++ b/issues/0004-add-a-readme.md
@@ -0,0 +1,14 @@
+---
+title: add a README
+status: closed
+labels: [documentation]
+created: 2026-09-18
+closed: 2026-10-17
+---
+There is no README. Write one covering:
+
+- what dew is (a local-first todo.txt TUI + CLI)
+- how to build/install it
+- where the config lives and what the keys do
+- the CLI subcommands (`add`, `do`, `rm`, `pin`, `wait`, `ls`, `flat`, `tui`)
+- the TUI keybindings, which right now are only discoverable by reading `tui.go` or pressing `?`
diff --git a/issues/0005-honor-xdg-config-home-for-the-config-path.md b/issues/0005-honor-xdg-config-home-for-the-config-path.md
new file mode 100644
index 0000000..6ba8449
--- /dev/null
+++ b/issues/0005-honor-xdg-config-home-for-the-config-path.md
@@ -0,0 +1,10 @@
+---
+title: honor XDG_CONFIG_HOME for the config path
+status: open
+labels: [enhancement]
+created: 2026-09-18
+closed:
+---
+`getConfigPath` builds the path by hand as `$HOME/.config/dew/config.json`, ignoring `XDG_CONFIG_HOME`.
+
+grrs was explicitly moved onto XDG conventions ("refactor: separate file locations"), so dew should match. `os.UserConfigDir()` respects XDG on Linux and has a sane fallback elsewhere.
diff --git a/issues/0006-add-config-json-example-and-a-friendlier-missing-c.md b/issues/0006-add-config-json-example-and-a-friendlier-missing-c.md
new file mode 100644
index 0000000..cf7f046
--- /dev/null
+++ b/issues/0006-add-config-json-example-and-a-friendlier-missing-c.md
@@ -0,0 +1,10 @@
+---
+title: add config.json.example and a friendlier missing-config path
+status: open
+labels: [documentation, enhancement]
+created: 2026-09-18
+closed:
+---
+`loadConfig` opens `~/.config/dew/config.json` directly and returns an error if it isn't there, so a fresh clone/build can't start at all and gives no hint about what to create. The struct has roughly 24 keys and none of them are documented anywhere.
+
+Add a `config.json.example` (mirroring grrs's `config.toml.example`), and either write a default config on first run or fail with a message that points at the example.
diff --git a/issues/0007-remove-dead-edittask-and-dedupe-the-cli-tui-sort-r.md b/issues/0007-remove-dead-edittask-and-dedupe-the-cli-tui-sort-r.md
new file mode 100644
index 0000000..c86aef7
--- /dev/null
+++ b/issues/0007-remove-dead-edittask-and-dedupe-the-cli-tui-sort-r.md
@@ -0,0 +1,12 @@
+---
+title: remove dead EditTask and dedupe the CLI/TUI sort + render logic
+status: open
+labels: [refactor]
+created: 2026-09-18
+closed:
+---
+Two bits of cleanup in the same area:
+
+`EditTask` in `actions.go` is defined with a doc comment but never called from anywhere — the TUI edits tasks through `$EDITOR` instead. Delete it or wire it up.
+
+The sort comparator and the due-date colour logic are copy-pasted between `main.go`'s `listTasks` and `tui.go`'s `refreshTasks`/`View`. They've already drifted — the CLI version has no terminal-width truncation — and they have to be kept in sync by hand. Hoist the shared sort/format helpers into one place.
diff --git a/issues/0008-document-that-the-todo-txt-priority-system-is-inte.md b/issues/0008-document-that-the-todo-txt-priority-system-is-inte.md
new file mode 100644
index 0000000..ef56bfb
--- /dev/null
+++ b/issues/0008-document-that-the-todo-txt-priority-system-is-inte.md
@@ -0,0 +1,12 @@
+---
+title: document that the todo.txt priority system is intentionally unsupported
+status: open
+labels: [documentation]
+created: 2026-09-18
+closed:
+---
+We intentionally do not support the todo.txt priority system — `(A)`, `(B)` and friends aren't something i want to use.
+
+Right now `ParseTask` still checks for a leading 3-character `(...)` token, but only so it knows where the `*`/`~` modifiers start. The priority token itself falls through into `Description`, so it renders as part of the task text and plays no part in sorting.
+
+This issue is just to document that omission (README and/or a comment on the parser) so it doesn't read as an oversight, and to decide whether the tag should keep showing up in the description or be stripped. No behavior change intended beyond clarity.
diff --git a/issues/0009-history-view-strips-completion-metadata-with-strin.md b/issues/0009-history-view-strips-completion-metadata-with-strin.md
new file mode 100644
index 0000000..c5c5ec7
--- /dev/null
+++ b/issues/0009-history-view-strips-completion-metadata-with-strin.md
@@ -0,0 +1,14 @@
+---
+title: history view strips completion metadata with string replacement
+status: open
+labels: [bug]
+created: 2026-09-18
+closed:
+---
+Rendering `done.txt` strips the `x <date>` prefix from the parsed description with:
+
+ strings.Replace(desc, "x "+parts[1], "", 1)
+
+It assumes `parts[1]` is a 10-character date. That happens to work for lines dew writes itself, but any task whose description contains the literal `x <date>` gets silently mangled, and the completed date is re-derived from raw fields on every frame (once in the measuring loop and again in the render loop).
+
+Fix: parse the `x <date>` prefix once in `ParseTask` (e.g. an `IsDone` + `DoneDate` field) and render from that.
diff --git a/issues/0010-add-tests-for-the-parser-smart-dates-and-recurrenc.md b/issues/0010-add-tests-for-the-parser-smart-dates-and-recurrenc.md
new file mode 100644
index 0000000..68c8b01
--- /dev/null
+++ b/issues/0010-add-tests-for-the-parser-smart-dates-and-recurrenc.md
@@ -0,0 +1,12 @@
+---
+title: add tests for the parser, smart dates, and recurrence
+status: closed
+labels: [testing]
+created: 2026-09-18
+closed:
+---
+There is no `*_test.go` anywhere in the repo.
+
+`ParseTask`, `compileSmartDate`, and `generateRecurringTask` are effect-free and full of edge cases — priority/modifier handling, `+project`/`@context` tokens, `due:`/`rec:`, `+Nd`/`+Nw`/`+Nm`/`+Ny`, month and year rollover, leap years — with zero coverage.
+
+Table-driven tests here would also give us somewhere to reproduce the bulk complete/delete ID bug before fixing it.
diff --git a/issues/0011-rec-silently-no-ops-on-malformed-input.md b/issues/0011-rec-silently-no-ops-on-malformed-input.md
new file mode 100644
index 0000000..be49a6d
--- /dev/null
+++ b/issues/0011-rec-silently-no-ops-on-malformed-input.md
@@ -0,0 +1,14 @@
+---
+title: rec: silently no-ops on malformed input
+status: closed
+labels: [bug]
+created: 2026-09-18
+closed:
+---
+`generateRecurringTask` discards the error from `strconv.Atoi` (`actions.go` around lines 152 and 155):
+
+ amount, _ = strconv.Atoi(recStr[:len(recStr)-1])
+
+A task with something like `rec:abc` ends up with amount `0` and unit `d`, so completing it silently generates a "next" task with an unchanged due date instead of failing. Same story for any non-numeric amount.
+
+Fix: validate the recurrence string and return a real error instead of generating a bogus task.
diff --git a/issues/0012-bulk-complete-delete-act-on-stale-task-ids.md b/issues/0012-bulk-complete-delete-act-on-stale-task-ids.md
new file mode 100644
index 0000000..0714713
--- /dev/null
+++ b/issues/0012-bulk-complete-delete-act-on-stale-task-ids.md
@@ -0,0 +1,14 @@
+---
+title: bulk complete/delete act on stale task IDs
+status: closed
+labels: [bug]
+created: 2026-09-18
+closed:
+---
+`Task.ID` is just the 1-based line number assigned by `ReadTasks`, and the bulk handlers in the TUI iterate `for id := range m.selected` calling `CompleteTask`/`DeleteTask` one ID at a time (`tui.go` around lines 631 and 691).
+
+Both of those re-read `todo.txt` and rewrite it from scratch, so as soon as the first task is removed every line after it shifts down by one and the remaining IDs point at the wrong tasks. Map iteration order is random in Go, so which tasks get clobbered changes run to run. Completing a task that has a `rec:` also appends a new line, which shifts things further.
+
+Single-cursor (`d`/`x`/`r` on one task) is fine, and pin/wait are fine because `ToggleModifier` rewrites in place without changing line order.
+
+Fix: resolve the selection to raw lines up front and do a single read + single rewrite, the same way the bulk tag editor (`P/C/D/R`) already does.