Skip to content

feat: archive and purge commands#2

Merged
Sanjays2402 merged 7 commits into
mainfrom
feat-archive
May 10, 2026
Merged

feat: archive and purge commands#2
Sanjays2402 merged 7 commits into
mainfrom
feat-archive

Conversation

@Sanjays2402

Copy link
Copy Markdown
Owner

What's new

Two new commands plus a shared duration helper.

tsk archive

Move Done tasks out of the active .tsk.md and into a sibling .tsk.archive.md in the same directory. Defaults to a 30-day cutoff so freshly-completed tasks stay in your face.

tsk archive                  # default: completed >30d ago
tsk archive --older-than 7d  # tighter cutoff
tsk archive --all            # everything Done
tsk archive --all --dry-run  # preview, no writes

Archived tasks get fresh sequential IDs in the archive file (continuing from the archive's existing max). Active task IDs do not renumber.

tsk purge

Hard-deletes tasks. Refuses to run without an explicit selection.

tsk purge --done                   # all Done tasks
tsk purge --done --older-than 90d  # only old Done tasks
tsk purge --id 17 --id 23          # specific tasks regardless of state
tsk purge --done --dry-run         # preview

Why both together

Both commands share --older-than semantics, so the duration helper lives in internal/store/duration.go and is reused. Pairing them in one PR also avoids the awkward window where archive ships without a way to nuke truly stale stuff.

--older-than accepts: Nd days, Nw weeks, Nm months (~30d), Ny years (~365d), plus multi-unit Go durations like 24h or 1h30m.

Safety story

  • Both files written atomically (existing tempfile + fsync + rename path).
  • archive and purge both support --dry-run and surface what they'd change before doing it.
  • purge refuses to run without --done or --id (exit 2). No "oops, I forgot a flag" footgun.
  • Missing --id N exits with an error so typos surface instead of silently no-op'ing.
  • Active task IDs stay stable across both commands, so external references (commit messages, notes) don't break.
  • Archive file gets a one-line # tsk archive header on first creation so it's obvious what it is.

Surface

Path Add Notes
internal/store/duration.go new ParseDuration
internal/store/markdown.go small Partition, ReplaceTasks
internal/commands/archive.go new tsk archive
internal/commands/purge.go new tsk purge
internal/commands/root.go wire register both commands
README.md doc new "Archiving and purging" section
internal/store/duration_test.go new table-driven cases + errors
internal/store/markdown_test.go extended Partition, ReplaceTasks
internal/commands/archive_test.go new end-to-end coverage

Tests

go vet ./..., gofmt -w ., go test ./... -count=1 all green. Coverage adds happy paths, dry-run, --all, --older-than, --id, refusal-without-selection, and ID continuation across two archive runs.

No new external deps. No force-push. Existing tests untouched.

Parses friendly forms (Nd, Nw, Nm, Ny) plus Go duration fallback for
multi-unit strings. Used by the upcoming archive/purge --older-than
flags.
Pure split (kept/removed) and an in-place slice swap that leaves
Header alone. Both are stepping stones for archive/purge so they can
compute the surviving slice externally without poking at internals.
Moves Done tasks to a sibling .tsk.archive.md. Default cutoff is
30 days completed; --all overrides, --dry-run previews. Archived tasks
get fresh sequential IDs in the archive file (continuing from its max);
active task IDs stay stable. Atomic writes for both files.
Hard-deletes tasks. Refuses to run without --done or --id so a typo
can't nuke the file. --older-than restricts --done to old completed
tasks, --dry-run previews, missing --id surfaces an error. Surviving
task IDs are preserved.
Adds a usage subsection covering tsk archive and tsk purge, the
.tsk.archive.md location, accepted --older-than syntax, dry-run
behavior, and the purge refusal-without-selection guarantee.
Table-driven ParseDuration coverage including the Nm-means-months
disambiguation. Partition + ReplaceTasks unit tests. End-to-end
archive/purge tests exercising happy path, --dry-run, --all,
--older-than, refusal without selection, --id, and ID continuation
across two archive runs.
@Sanjays2402 Sanjays2402 merged commit ef66d6b into main May 10, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant