Changelog
All notable changes to yman are recorded here. The format follows
Keep a Changelog, and versions follow
Semantic Versioning. How a release is cut is described
under Releasing in the README.
Unreleased
Added
skills/yman-task/, a second agent skill:/yman-task <text>files the user’s own words as tasks taggeduser-requestandneeds-agent, quoting the original, so the agent that picks one up re-checks it before working.
0.7.0 - 2026-10-13
Added
Cargo.tomldeclaresrust-version = "1.88", the first release with let-chains, which the code uses.
Changed
yman guideand the skill say thatyman plan’s rows are enough to pick the next step, so agents stop runningshowon every listed step.- Titles and tags refuse the Unicode line and paragraph separators and the
bidi override and isolate characters, which could make
lsoutput misleading, with the existing control-character errors; a refused tag is shown escaped. - Git’s multi-line stderr quoted in
git <sub> failed: …(and so inwarning: refresh failed: …) has its continuation lines indented by two spaces and blank lines dropped, andsync’s push-refusal line is indented two spaces, so every stderr line yman writes carries a prefix or continues one. - Every stderr line now carries its prefix or continues one: the
rmandtags rmprompts moved from stdout to stderr,refreshed: N new commit(s)becamenote: refreshed N new commit(s), the hook advice is anote:and an unreadable hook awarning:, andsyncintroduces its list of conflicting files withnote: unmerged files:. yman add --sectionswalks the task history for taken ids once per run instead of once per section.
Removed
- The
cannot attach <path>: no file nameandstatuses.terminal marks every status terminalerrors, which could never fire: thenot a regular filecheck and the rule thatstatuses.defaultis not terminal refuse first. - The
no editor configured; set $EDITORerror, which could never fire: the editor resolver always falls back tovior refuses earlier.
Fixed
- A fresh
yman initwhose push lost the race and whose refetch then failed no longer exits 1 with the tracker half set up: it installs hooks, prints the summary and warnsnot published to origin (fetch failed); run: yman sync. add --sectionsreads a line like```x``` inlineas text, as CommonMark does, instead of opening a fence and refusing the file withunclosed code fence.attach --force A.pngovera.pngon a case-insensitive checkout (macOS, Windows) recordsA.pngin git, matchingm.yml, instead of leaving the index ata.pngand every Linux clone with a dangling attachment.attachrefuses Windows reserved device names (CON,NUL,COM1,LPT1… with or without an extension, any case), which Git for Windows cannot check out, with the existingis not portableerror.- Hooks defined in git configuration (
hook.<name>.command, git 2.54+) no longer fire for yman’s commits, merges and checkouts in.yman, and yman’s ownupdate-ref,fetchandpushno longer run the user’sreference-transactionhook.yman hooksrefuses an emptycore.hooksPath, which turns git’s hooks off, instead of installing into.git/hooksand reportinginstalled, and no longer trims a whitespace-only one. - A comment line after an unknown
|+block inm.ymlis dropped like every other comment, instead of surviving when a blank line followed it. An indented#line inside an unknown key’s block is content and is kept. - An
m.ymlvalue with a line of only Unicode whitespace (a U+00A0, say, fromset --link) is written double-quoted, so it no longer reads back empty or chomped. - A blank or
-assigneeinm.ymlreads as unassigned (ls --assignee -lists it,--jsonprintsnull), andset -a -/add -a -unassign instead of storing a literal-. rm,setand the status verbs no longer exit non-zero after their commit has landed when reading the tasks back for the “no longer waits” report fails; that is now a warning, so a script that retries on failure does not apply the change twice.- An aborted
yman add -einto a closed status under an archive config also removes the status directory it created, and a copy failing part way throughyman attach(a full disk) restoresf/as it was, including files--forcehad already replaced, instead of leaving the earlier copies behind. - A stopped
syncno longer says a task “was closed to two different statuses” when the second folder is only a file left under the task’s old name after a retitle or close; the note now needs two folders that carry the task under two different closed statuses. - A
t.mdwith CRLF line endings no longer keeps a stray\r\nat the top of its body, and a UTF-8 BOM no longer breakst.md(must start with "# Title") or hidesm.yml’s first key. - The comment count in
lsandls --jsonleaves out unparsabled.mdchunks. yman tags renamereplaces every spelling of the old tag on a task, not only the first ([UI, ui]renamed toxgave[x, ui]).yman tags --json rename …andyman sync --abort --no-pushare refused as usage errors instead of silently ignoring the extra flag.- Shell completions offer file paths for
--body-fileand--sections. rmon one clone and a first comment or attachment on the same task on the other no longer merge into a folder holding onlyd.md(orf/) that no command could load: sync drops it withnote: dropped <rel>: task <id> was removed on one side; ….yman syncno longer dies after the merge withNot a valid object name HEAD^2when one merge needs both a rejoin and a remnant drop, which left the unloadable remnant for the next sync to push. Nor does it die withpathspec … did not match any fileson a removed task’s folder thatrmleft holding only an ignored file such as*.swp; there is nothing to drop there, and the sync goes through.yman detachmatches the attachment name ignoring case, asattachdoes:detach 3 A.pngremoves ana.png(under its stored spelling) instead of failing withno attachment "A.png" on task 3.yman initwhose first push fails no longer stops before--hooksand the summary: the tracker exists locally, so it finishes and warnsnot published to origin (<why>); run: yman sync.yman initnames the history it found broken (refs/yman/localorrefs/tasks/main on origin) instead of always blaming origin.- The merge driver’s path is single-quoted in git config, so a binary under a
path containing
",$, a backtick or backslashes still runs. - Repository discovery works with git older than 2.31, which echoes
--path-format=absoluteback instead of honouring it. yman syncpushes the commitrefs/yman/localheld when the push began, by sha, and records that commit as published. A commit made by another command during the push used to be marked published without reaching origin, and autosync then never pushed it.- A sync renumber that fails part-way resets
.ymanto the commit before it (renumber aborted; .yman restored) instead of leaving half-moved folders for the next sync to snapshot. yman sync --continuereports how many commits the merge pulled instead ofpulled 0, gives the git-identity hint when committing fails for want of one, and no longer follows symlinks while scanning for conflict markers (a committed symlink loop recursed until it failed).yman syncandyman initno longer overwrite the main repository’sFETCH_HEAD.- Run from a git hook or alias, yman strips every variable
git rev-parse --local-env-varslists, plusGIT_NAMESPACE, instead of five of them, andyman gitstrips the same set, so it acts on.ymanrather than on the repository the hook was running for. Ayman gitwhose git is killed by a signal exits128 + signalinstead of 1. yman attachneeds--forcewhenever the task already lists the name, even when its file is gone, and compares names ignoring case:--force A.pngovera.pngreplaces the file and the entry rather than adding a second entry. Names a Windows checkout cannot hold (:*?"<>|, control characters, a trailing.or space) are refused.yman add --sectionsrefuses a code fence that is never closed (<source>: line <n>: unclosed code fence) instead of swallowing every later heading, and a fence line with an info string (```sh) inside an open block no longer closes it. Fences follow CommonMark: a longer fence holds shorter ones.m.ymlround trips: a value with Unicode whitespace at an edge (U+00A0) is quoted so it reads back intact; a comment after a quoted scalar (status: 'todo' # x) no longer fails as unterminated; the libyaml escapes\a \b \v \f \N \_ \L \Pare read; and an unknown|+block keeps its trailing blank lines on rewrite..yman/config.tomlrefuses a blank, edge-whitespace or repeatedstatuses.listentry. A blank status used to letadd -s ''commit a task whosem.ymlread back as broken.--relateand--waits-ontrim their values and refuse a blank one, andsetrefuses a task relating to (or waiting on) itself.- Id generation reports an exhausted id space instead of looping forever
(
ids.scheme = "random"with everyrandom_lenid taken) or overflowing (seq/authorpastu64::MAX). - Ids are sorted by a total order: plain numbers, then
prefix-Nby prefix and number, then the rest lexically. The old pairwise rules could form a cycle (9 < 10 < 5-a < 9), on which Rust’s sort may panic. The one visible change is that aprefix-Nid such asz-1now sorts before a random id such ast-7f3a. yman refreshon a dirty.ymanprints one note,note: .yman has uncommitted changes, refresh skipped, instead of following it with a secondnote: worktree has uncommitted changes.yman ls --helpdescribes-aas including closed tasks (any terminal status) rather than “the final status”, which predates terminal sets.yman set -anow trims its value and treats a blank one as unassigned, asyman add -aalways has; it used to store" bob "or" "verbatim.--helpforedit,add -eandcomment -enames$VISUALbefore$EDITOR, the order the editor is looked up in, andyman --helplists every editor-free way to give a body (add -m,set --body-filewere missing). The agent manual and the skill listplanamong the commands that take--jsonand say thatlsandplancarrybodyonly with-l; the docs add theautosynckey ofstatus --json, theyman.autosyncsettinginitwrites, and thegit worktree add failederror.
0.6.1 - 2026-10-04
Fixed
yman ls | head -1, and any other output piped into a reader that quits early, now ends quietly the way git and coreutils do (killed by SIGPIPE, shell status 141) instead of panicking withfailed printing to stdout: Broken pipeon stderr.- A multi-id verb that fails part-way (
yman done 32 99) now prints the close report — epic warning,no longer waitsnotes — for the ids it did commit before returning the error; it used to drop it.yman rmof an open blocker now prints theno longer waitsnote for a blocked task it freed, as closing the blocker would.yman attachchecks every source before copying any, so a missing second file or two sources with the same name (nowattachment "<name>" given more than once) no longer leave an untracked copy of the first one underf/. yman hooksnow finds the hooks directory the way git does, throughrev-parse --git-path hooks. Acore.hooksPathof~/.githooksused to be taken literally, so the hooks landed in<project>/~/.githookswhere git never runs them. A hook yman cannot read — permission denied, content that is not UTF-8, a dangling symlink — used to count as absent:installoverwrote it, or wrote through the link and created its target. It is now refused like any foreign hook (hook <name>: cannot read <path>: <why>; not replacing it) andhooks statusreports it asunreadable.- The user’s
post-commit,post-merge,post-checkoutandpost-rewritehooks no longer run for yman’s own commits, merges and fast-forwards in.yman, nor for thegit worktree addthat creates it.--no-verifyonly skipped the pre-hooks, so a husky, lefthook or LFS hook that wrote files left.ymandirty: refresh then backed off for good and the next snapshot committed the junk. Every git call in.ymannow runs withcore.hooksPath=/dev/null; hooks in the project itself,yman hooksincluded, fire as before. - A push that origin refuses — a
pre-receivehook declining it, a protected or denied ref — is no longer mistaken for origin having moved.syncused to retry it three times and end inorigin keeps moving, autosync used to sayorigin has new task commits, andinitthen failed resetting to arefs/yman/remotethat did not exist. The push now runs with--porcelainand onlyfetch first,non-fast-forward,incorrect old value providedandreference already existscount as the race; anything else fails at once withpush failedand git’s reason on stderr. Aninitwhose push loses the race but finds nothing to adopt on refetch pushes again instead of resetting. - A title containing a newline, a carriage return, a tab or another control
character is refused by
add,set --title,add --sections,add -eandeditwithinvalid title "<t>"; titles must not contain line breaks or other control characters;editleaves the text on disk to be fixed, as it does for a file that does not parse. A newline used to be split into a title and a body line int.mdand to break the commit subject over several lines. - An
add -ewhose editor fails, or whose editedt.mddoes not parse, no longer leaves an untracked task folder behind forlsto list and the nextsyncto publish. The folder is removed and the error saystask not added. comment -eno longer writes a fixedyman-comment-<id>.mdin the temp dir. On a shared/tmpa symlink planted at that path made it truncate another file, and two runs on one id shared the file. The temp file now gets a fresh name, is created exclusively with mode 0600 and never through a link, and is removed on every path; a refused editor creates none. A failing editor sayscomment not addedinstead offile left as is.- The docs no longer call
comment <id>with stdin safe for scripts: without-mit reads stdin to end of file whenever stdin is not a terminal, so a caller that leaves stdin open blocks.docs/commands.md,yman guideand the skill now say to pass-mor redirect stdin. - An id a sync renumber moved a task to is no longer handed out again after
that task is removed. The history scan read the renumber’s
git mvas a rename, not an add. - A sync merge that conflicts in
config.tomlno longer locks out every command:sync --abortandsync --continuestill run, mutating commands exit 3 as documented instead of 1, and--continuerefuses a resolution that does not load. yman initre-attaches a detached or misplaced.ymanHEAD torefs/yman/local, as the preflight error advises, instead of failing with that same error. It refuses when HEAD has commits or edits the ref lacks.- Commands run from inside
.yman—cd $(yman path 14)— find the project instead of failing with.yman is not initialized, andinitthere no longer nests a second worktree at.yman/.yman. yman initfrom a linked worktree of the project refuses to create a second.ymanon the same ref; git does not guard a--detachcheckout.- Under a localized git,
initandsyncagainst an origin with no tasks no longer fail withfetch failed, and re-applying an identical change is no longer an error. Every git child now runs withLC_ALL=C. sync --continueno longer takes a conflict git could not mark up (a binary attachment, modify/delete, or anm.ymlwhose merge driver path is gone) as resolved, which kept ours and dropped theirs without a word. Such a file must be staged by hand, andsyncnow prints git’s stderr from the failed merge, so a stale driver’sNo such file or directoryis visible.
0.6.0 - 2026-10-04
Added
yman plan <id>: a task, a per-status count over every task relating to it, and the open ones sorted likels, withwaits on <ids>on a blocked step.-a,-n,-land--jsonas onls.--waits-on <id>onaddandset: relate to a blocker and set statusblockedin one commit. Needsblockedinstatuses.list.- Closing a task (
done,cancel,move/set --status) notes on stderr each blocked task that now waits on nothing open, with theyman movethat frees it. Nothing is moved automatically. add --sections <path|->: one task per# Titlesection of a markdown file, body up to the next heading, every other flag applied to each. A heading inside a code fence is body text; a bad file fails before any id is minted.- Closing a task tagged
epicwhile tasks relating to it are still open warns on stderr and names them; the close still goes through.
0.5.1 - 2026-10-01
Added
- Published to crates.io as
yman-tracker:cargo install yman-tracker.Cargo.tomlgained the registry metadata and anexcludelist, and the release steps in the README end withcargo publish.
Changed
- The README says where a
cargo installleaves the skill (the crate’s unpacked source under~/.cargo/registry/src/) and how to copy it into a project or into~/.claude/skills/.
0.5.0 - 2026-10-01
Added
yman ls --related <id>keeps the tasks whoserelatedlist carries that id, so the steps of a plan can be listed from their epic without editing it. It ANDs with the other filters; an id nothing relates to lists nothing.yman showprintsrelated by:, the tasks whoserelatedcarries this one, closed tasks included, andshow --jsonalways carriesrelated_by.relatedstays one-way and nothing is stored;showreads everym.ymlonce to find them.- The agent manual (
yman guide, the skill and the README’s “Scripts and agents”) describes plans: an epic task, steps that--relateit, priority for order, andblockedplus a relation to the blocker for a step that waits.ls --relatedandrelated by:read the one-way relation from either end, so an epic is never edited when a step is added. The guide’s line cap intests/cli.rsrose from 60 to 72 to fit the section. yman init --autosync push(git configyman.autosync): every command that changes a task pushes it to origin once it has committed, and reportsnote: pushed N task commit(s)on stderr. It never fetches or merges; when origin has moved it warnsorigin has new task commits; run: yman sync, and a failed push is a warning, never the command’s exit code.yman statusshowsautosync: pushwhen it is on, andstatus --jsonalways carries"autosync".- The agent manual (
yman guide, the skill and the README’s “Scripts and agents”) now says when to runyman sync: before any status change, so an agent sees another clone’s claim or close before overwriting it, and after claiming and closing, to publish. Before, it describedsyncbut never asked for it, so agents left task history unpushed. Theclaim-taskandclose-taskskill evals check both. - A documentation site, built with mdBook from the README,
docs/and the changelog and published to GitHub Pages: https://brillliand.github.io/yman-tracker/.scripts/book.shbuilds it locally and fails on broken links or anchors.
Fixed
docs/commands.mdanddocs/errors.mdwere checked against the code and corrected where they had drifted: the mutating-command list, theinitcommits,refreshnotes, exit codes afteryman gitand forno merge in progress, and messages the tables were missing.docs/storage.mdanddocs/setup.mdwere corrected the same way: what the spike script actually enforces, howt.mdandm.ymlare written, where hooks go, which commands use the network, and the filesystem notes added in the previous entry, which overstated one gap and missed the Windows ones.- The three copies of the agent manual (
yman guide, the skill and the README’s “Scripts and agents”) agree again: the task-list rule no longer asks for an assigneelsdoes not print,rm -fand the duplicate-id recovery are in all of them, and the README no longer saysinitadds one config line or thatsyncis the only command using the network. yman editretitling a closed task in a version 2 repository no longer fails halfway withpathspec ... did not match any files; the task is renamed inside its status directory instead of being moved to the top of.yman.yman syncrenumbering a colliding task that is closed, in a version 2 repository, keeps it in its status directory. It used to move the task to the top of.ymanwhilem.ymlstill said it was closed.
Changed
- The README now calls Windows untested, and
docs/storage.md§5 records the known case-insensitive and Unicode-normalization pitfalls on macOS and Windows.
0.4.0 - 2026-09-23
Added
yman completions <shell>prints a completion script for bash, zsh, fish, elvish or PowerShell, andyman man [--dir <dir>]prints the man page or writes every page to a directory. Both are generated from the CLI definition itself and work outside a repository.yman initin a repository with nooriginsets up a local-only tracker instead of refusing.statusreports it,syncsays to connect a remote, andyman init --remote <url>does so later.
Fixed
showprints the discussion in timestamp order, so two comments written concurrently on two clones no longer appear out of order after a sync. Unparsable chunks keep their place;d.mditself is unchanged.
Changed
- The yman skill formats a task list as a table and suggests what to take next.
0.3.0 - 2026-09-23
Added
yman ls -lprints each task’s body, indented under its row;--jsongainsbodyonly under-l.
Fixed
synckeeps a moved task’s new files with the task: a first comment or an attachment added on another clone under the old folder name follows a retitle or a close instead of stopping with exit 3 or being orphaned.
Performance
yman log <id>finds a task’s history by a pathspec on its id rather than by rebuilding the rename graph, and no longer shows unrelated tasks’ commits when git paired two folders as a rename. About 400 ms down to 125 ms on the synthetic 1000-task repository.
0.2.0 - 2026-09-23
First tagged release.
Added
- The tracker itself: task history in
refs/yman/local, checked out at.yman/;init,add,ls,show,edit,set,rm,comment,attach,log,status,path,sync,refresh, thegitpassthrough and the optional git hooks. move,cancelandreopen; state verbs take several ids.- Named status roles and a terminal set in the config; closed tasks are
archived into a per-status folder, and
lshides every terminal status. - The
tagscommand, which renames and removes a tag across all tasks; tags are validated and lowercased, andls -tmatches case-insensitively. --jsonoutput forshowandstatus.yman guide, and a description for every flag.- Bodies set without an editor on
addandset,set -mappending a comment in the same commit, andeditrefusing thevifallback without a terminal. addaccepts an assignee, links and related tasks.show -ncaps the discussion;lsfilters and limits without reading the whole backlog.commentandattachtake the author fromYMAN_ACTOR.- Exit 4 when a task id does not exist.
syncmergesm.ymlfield by field and union-merges its list fields, and rewrites related references when it renumbers a task.- The yman agent skill and its evals, and CI running the quality gate.
Fixed
synckeeps a task’s id through a retitle, only renumbers ids minted locally, and follows tasks into status directories.- Related references stay consistent.
- An attachment whose file is gone is marked as such.
- Attachments matching
.yman/.gitignoreare staged. - The
d.mdentry header is guarded on write and on read. - Unknown
m.ymlkeys survive a rewrite. - An author prefix that would break folder names is rejected.
statuses.doneis required once more than one status is closed.
Performance
- Refs are read from disk instead of by spawning
git. - A task is looked up from folder names alone.