Adds a dot setup subcommand family (idempotent, re-runnable machine-setup tasks) with a folders task that brings the 8 standard XDG user directories under a fixed short-name convention (Desktop -> .desktop, Documents -> doc, Downloads -> dwn, Music -> mus, Pictures -> pic, Videos -> vid, Templates/Public -> .ignoreme), hardcoded rather than read from ~/.config/user-dirs.dirs -- that file is a separate, manually tracked dotfile whose values can drift or go stale, and the migration must not depend on it being correct. The nested Pictures/Screenshots folder is renamed to pic/screenshots in the same pass. Content left behind in a legacy full-named folder by a fresh XDG-defaults install -- empty or not -- is merged into its short-named replacement. A same-named collision between a legacy folder and its target is never overwritten: it is skipped, reported, and the legacy folder is left in place rather than deleted while something in it could not be merged. --dry-run previews what would move/skip without touching the filesystem. xdg-user-dirs-update runs once afterward to notify running apps/portals.
5.0 KiB
spec
| spec |
|---|
| dot-setup-folders |
What to build
A new dot setup subcommand family, following the project's existing
nested-subcommand dispatch convention. Bare dot setup (no arguments) runs
every machine-setup task unconditionally; dot setup <task> runs just that
one task. The only task that exists yet is folders.
dot setup folders brings the 8 standard XDG user directories under the
project's short-name convention (Desktop→.desktop, Documents→doc,
Downloads→dwn, Music→mus, Pictures→pic, Videos→vid, Templates and
Public both →.ignoreme). The desired short names live in a tracked
user-dirs.dirs file (a plain dotfile, not generated from a table each run).
A separate small hardcoded table maps each of the 8 standard XDG categories
to its legacy full-named folder, used only to locate content an XDG-defaults
install would have left behind, and merge it into the already-tracked
short-named target.
This slice covers the core happy path: a legacy folder found empty (strictly:
no entries at all, including dotfiles/metadata) is merged into its
short-named target silently, with no confirmation needed. As part of the same
Pictures→pic pass, a nested Screenshots folder is renamed to lowercase
screenshots, landing at pic/screenshots. After all folder moves complete,
run xdg-user-dirs-update (no arguments) once to notify running apps/portals.
~/wrk gets no XDG variable of its own and is out of scope for any mapping;
the existing ad hoc ~/Projects folder is left alone.
Non-empty legacy folders and filename collisions are out of scope for this slice (covered by later tasks) — for now it's acceptable for a non-empty legacy folder to be handled in whatever minimal way unblocks the empty-folder path (e.g. left untouched with a message), since the confirmation gate and collision safety are built out next.
Wire the new command into the project's standard subcommand checklist: a
_dot_setup_usage help function reachable via dot setup help (and
dot setup folders help for the nested task), the completions/help-glob
duplication point, and a README command-table row.
Acceptance criteria
dot setup folderson a fresh scratch$HOME(all 8 legacy folders present and empty) renames them to their short-name targets per the mapping table, includingPictures/Screenshots→pic/screenshots, and leaves the trackeduser-dirs.dirsshort names in place- The fake
xdg-user-dirs-update(PATH-prepended, logging its invocation per the project's existing fake-sudo/fake-pacmantesting pattern) is invoked exactly once after a successful migration - Bare
dot setupon a fresh scratch$HOMEruns thefolderstask as part of running everything dot setup folders helpanddot setup helpprint usage and make no filesystem changes- Re-running
dot setup foldersafter a clean migration is a no-op (idempotent) ~/.github/README.mdhas a command-table row fordot setup(and itsfolderstask) with paths relative to$HOME~/.config/dot/tests/dot.fishcovers the above cases andfishtape ~/.config/dot/tests/dot.fishpasses
Implementation Notes
- The desired short names for
dot setup foldersare read directly from the tracked~/.config/user-dirs.dirs(parsed viagrep/string match, not sourced as shell), per the parent spec's decision that this file is the single source of truth. This machine's realuser-dirs.dirswas deliberately left untouched/untracked and no live migration was run against this machine's actual home directory — the user chose "code + tests only" scope for this task (a real rename of~/Desktop,~/Documents, etc. is a separate, explicit action to take later), so only the scratch-$HOMEfishtape fixtures exercise the short-nameuser-dirs.dirscontent. Tracking the real file and running the real migration remains open. - During
/review-uncommitted, the spec-fidelity pass caught a real bug: the nestedPictures/Screenshots→pic/screenshotsmove ran unconditionally, before checking whetherPicturesheld other, unrelated content — so aPicturesfolder with bothScreenshots/and some other file got partially mutated (Screenshots pulled out) while still being reported as "left in place." Fixed by gating the Screenshots move on the rest of the folder being empty too; added a regression test for this case ("Screenshots is not peeled off... when Pictures still has other content"). - Completions (
~/.config/fish/completions/dot.fish) got adot setupblock mirroringdot kde's per-subcommand completion entries, even though the task's required "completions/help-glob duplication point" is already satisfied automatically by the existing generic directory glob (no changes were needed there fordot setup/dot helpto discover the new nested command). The added completions are a small polish addition beyond the strict letter of the acceptance criteria, consistent with the existingkdesubcommand's treatment.