Changelog
What changed, and when.
[Unreleased]
0.5.0 (2026-08-13)
Breaking changes
- refactor!:
minato move --jsonnow emits a summary,{"reports": [...]}, as every other action command does, rather than the single report it emitted before. A move that succeeds while the manifest of its tree cannot be written is reported in that summary, so the answer says why the run exited non-zero. Read.reports[0]for what was previously the whole document.
Features
- feat: add
minato manifest, which records where clones sit and restores a tree from that record. The record is a.minato.tomlfile in the root it describes, so the file is the root reference: paths are relative to it, restoring targets that directory, and a tree carries its layout between machines without any configuration.writerecords every clone found and never drops one another machine may hold,applyclones what is recorded and missing here and reports a clone sitting elsewhere unless--relocateis passed,diffreports what the record and the tree disagree about, andforgetstops recording one repository without touching its clone.cloneandmovekeep an existing manifest up to date, in the browser as well as on the command line, while the commands that only read a tree leave it alone. Any command run from inside a recorded tree works on it (#99). - feat: lead the
minato statustable withPATH, followed byREPOSITORY,GROUP,STATE, andNOTES, and order rows by group, then by path, so clones sharing a directory read together. Repositories with no clone, and clones sitting directly in a root, come last rather than first. The ordering is shared, so--jsonand thefetchandupdatereports follow it too. The interactive view gains the same leading path column, and ordering it by group withsnow follows the same group, then path order, leaving the ungrouped rows last as well (#98).
0.4.1 (2026-08-07)
Documentation
- docs: clarify wording across the README, documentation site, CLI help text, and error messages.
0.4.0 (2026-08-04)
Breaking changes
- fix!:
--owner,--group,--state, and--include-externalnow belong to each command rather than tominatoitself, so they must be written after the command:minato status --group perso, notminato --group perso status, which clap now refuses with a tip naming the right form. Every documented example already used the first form. This is what lets a command’s help and its completion script offer only the conditions it can act on; naming one it cannot is still refused with the explanation that names the command which does take it, rather than a bare “unexpected argument” (#91).
Features
- feat: report a stale shell completion script in
minato doctor, comparing each one it finds against what the binary would generate now and printing the command that rewrites it, so a script left offering the commands of an older release is noticed rather than quietly wrong (#89). - feat: name any completion script the shell installer has just made stale, with the command that rewrites it, since an install is the moment the command surface changes; a script still matching the installed binary is passed over in silence (#90).
- feat: install a completion script with
minato completions <shell> --install, which writes it where the shell reads it and edits the shell’s configuration only where the file alone is not enough, inside a managed block that re-running replaces and--uninstallremoves.--dry-runreports every path either would touch. The shell is taken from$SHELLwhen it is left out, and printing the script to standard output is still what happens without a flag. - feat: install zsh completions to Oh My Zsh’s custom directory, or to Homebrew’s
share/zsh/site-functionswhen the prefix has one and it is writable, before falling back to~/.zfuncwith anfpathline. The first two are onfpathalready, so nothing is added to~/.zshrcfor them. - fix: report a stale copy that cannot be removed, rather than failing on it. One of the places swept is Homebrew’s prefix, which is outside the home directory and may belong to another user, and a file there is not reason enough to fail an install that has otherwise worked. Nothing is ever removed with elevated rights.
- feat: keep an install where it is. A script already on disk is the one that is updated, wherever it is, even once the machine has gained Oh My Zsh, Homebrew, or bash-completion and a first install would now choose elsewhere; copies in the other known places are swept so none is left to shadow it. Moving one is
--uninstallfollowed by a fresh install. - feat: leave an unchanged script alone, reporting
Already currentrather than rewriting a file that already matches what this binary generates.
Bug Fixes
- fix: say where a completion script goes, printing the destination for the shell asked for on standard error alongside the script on standard output, so
minato completions zsh > _minatowrites the script alone and the instructions still reach the terminal; the help no longer declines to answer, and the zsh instructions name the oh-my-zsh directory that needs no further setup (#88). - fix: honour
$XDG_DATA_HOMEand$XDG_CONFIG_HOMEwhen working out where a completion script lives. Both were hardcoded to~/.local/shareand~/.config, so on a machine that sets either,minato doctorreported on a file nobody had, and the installer’s stale-script check looked in the wrong place. - fix: follow
$ZSHand$ZSH_CUSTOMonly when they point inside$HOME. Oh My Zsh exports both, so a run with a different$HOMEwould otherwise install into, and on uninstall delete from, a home nobody named.
0.3.0 (2026-08-03)
Features
- feat: let groups nest as the directory tree does, so a clone in
perso/appsis in the groupperso/appsrather than reported asperso, with--group persokeeping everything filed beneath it and--to-groupand--into-groupaccepting a nested group. - feat: print a shell completion script with
minato completions <shell>, for bash, zsh, fish, PowerShell, and elvish, generated from the command definitions so it can never offer a command the binary does not have (#82). - feat: install on Windows with one line,
powershell -ExecutionPolicy ByPass -c "irm https://m.canouil.dev/minato/install.ps1 | iex", verifying the download againstSHA256SUMSas the shell installer does, installing into%LOCALAPPDATA%\Programs\minatowithout administrator rights, and adding that directory to the userPATHwithout expanding away a%VARIABLE%already written there (#84). - feat: publish a native Windows on ARM binary,
minato-<version>-aarch64-pc-windows-msvc.zip, so an ARM64 machine installs it rather than running the x86-64 build under Windows’ emulation (#85).
Bug Fixes
- fix: refuse a
--into-group, or a group typed in the browser, that names its way out of a root, which--to-groupalready refused rather than cloning there. - fix: refuse
--group,--state, and--include-externalon the commands that never scan local clones, rather than accepting them and answering with the unnarrowed set:minato list --group persolisted every repository, including for a group that does not exist.listandsync-forknow take--owneralone, andmove,refresh,doctor,auth status, andcompletionsnarrow by nothing.
0.2.0 (2026-07-22)
Performance
- perf: shrink the repository listing query, halving the page size and no longer counting release downloads, so GitHub is less likely to answer an account with a 502; the download count is dropped from
minato listas a result (#68).
Bug Fixes
- fix: serve the last cached repositories with a warning when GitHub is briefly unavailable while listing, so a passing 502 or unreachable host no longer fails a whole run (#68).
0.1.0 (2026-07-22)
First public release of Minato.
Features
- feat: compare every repository a GitHub account owns against the clones on disk, reporting each as not backed up, in sync, ahead, behind, diverged, or local only, with the reason it is in that state (#6, #7).
- feat: clone missing repositories, fetch every clone, and fast-forward those that are strictly behind and clean, each with
--dry-runand each reporting what it deliberately left alone (#8, #9). - feat: sync forks with their upstream through GitHub’s merge-upstream with
minato sync-fork, fast-forward only and with--dry-run, leaving a diverged fork reported rather than merged (#36). - feat: report how far a fork trails the repository it was forked from (#13).
- feat: group repositories by the directory they sit in, with
--groupto select and--into-groupto place new clones, andminato move --to-groupto move an existing one (#10, #14). - feat: hide forks and clones of untracked owners by default, with
--include-forksand--include-externalto show them (#30). - feat: browse the whole comparison interactively with
minato tui, cloning the highlighted repository withcinto a group you choose or create and refreshing the row once it lands (#11, #56, #57). - feat: narrow every command with repeatable
--owner,--group, and--state, and emit machine-readable output with--json(#7). - feat: configure how long provider responses stay cached with
cache.ttl, defaulting to fifteen minutes (#27). - feat: show the latest release’s download count in
minato list, surfacing metadata already fetched (#53). - feat: install with one line,
curl -fsSL https://m.canouil.dev/minato/install.sh | bash, with releases shipping aSHA256SUMSasset and build provenance the installer andgh attestation verifycan check (#51). - feat: generate the command reference from the command-line definitions, gated in CI so the documented commands and flags cannot drift from the binary (#37).
Bug Fixes
- fix: retry a transient stream or connection reset while listing, so one cancelled response no longer fails a run enumerating several accounts (#31).
- fix: report a persistent server error or an unreachable host as such when listing gives up, instead of always calling it a rate limit (#49).
- fix: report symlinked directories the scan does not follow, so a projects tree kept behind a symlink is explained rather than showing an empty result (#32).
- fix: recognise a bare repository or mirror and report it rather than silently ignoring it, since it has no working tree to compare as a clone (#33).
- fix: report a clone the scan cannot read as a failure rather than dropping it, so its remote is no longer mislabelled “not backed up” (#48).
- fix: skip a clone with no
originremote when fetching rather than attempting it, so one never-published clone no longer fails the wholefetch(#39). - fix: report unreadable roots and skipped paths from
clone,fetch, andupdatetoo, not onlystatus, so a mistyped root is never silent (#42). - fix: escape quotes and backslashes when building the fork-comparison GraphQL query, so an unusual branch name cannot corrupt the request (#40).
- fix: reject
.or..as a repository owner or name, so a crafted identity from cache or configuration cannot build a directory-escaping path (#41). - fix: reject a
move --to-groupname that is not a plain directory (empty,.,.., or containing a path separator), so a clone cannot be moved outside its root (#46). - fix: pass the clone destination to git as its real path rather than a lossy display string, so a non-UTF-8 path is not mangled (#45).
- fix: give each process its own cache temporary file, so concurrent runs writing the same key cannot publish a torn entry (#47).
- fix: list each account once when a login appears under both
usersandorgsor in a different case, so repositories are no longer fetched, reported, and cloned twice (#44). - fix: apply
--owneronlistandsync-fork, which previously ignored it (#50). - fix: in the TUI, clear a status message on the next key so the key hints return, refresh the table after a fetch or update, and open the screen before the first scan so it no longer appears to hang on start (#54).