Repository navigation
v0.2.6
Every fix in this release is in mem init / mem uninstall. The documented promise for those two
commands is that uninstall reverses exactly what init wrote and leaves everything else alone; five
separate defects broke it, one of them by deleting the user's own text.
Fixed
mem uninstallcould delete a block of the user's own file -- the per-tool marked block was located with a bareindexOf(start)/indexOf(end)pair, which pairs the first start marker with the first end marker even when they do not belong together. A hand-edit, an interrupted write, or a merge conflict can leave an orphaned<!-- token-goat-mem:<tool>:start -->behind with no end of its own; uninstall then paired that orphan with the end marker of the real block further down and removed every byte in between. In a CLAUDE.md shaped# My notes/ orphan / user prose / real block, the user prose went with it. The block is now located by scanning every start marker and taking the first one that resolves to a complete pair, which is how the sharedAGENTS.mdblock has always been located -- the per-tool path simply never got the same rule. A stray end marker sitting ahead of the real block no longer makes install append a second copy, either.- A reinstall never upgraded the shared
AGENTS.mdblock's body --codex,copilot-cli, andcopilot-vscodeshare one reference-counted block, and the writer returned early as soon as the tool was already named intools=. A body written by an older version of mem was therefore left in place forever: the per-tool blocks were replaced with current text on every reinstall and this one silently drifted away from them. It is now rebuilt from the same constant every time, and returns the file untouched only when the rebuild is byte-identical. mem initrewrote the formatting of files it does not own --.claude/settings.jsonwas round-tripped throughJSON.stringify(parsed, null, 2), so a config indented with four spaces came back indented with two, with its key layout and any non-canonical spacing gone..vscode/tasks.jsonandkeybindings.jsonwent through jsonc-parser'smodifywith aformattingOptions, which reformats the entire containing array, so a hand-written one-line{ "key": "ctrl+q", "command": "noop" }came back exploded across five lines. Neither file is mem's to restyle. Both paths now edit text rather than reserialize an object:modifywith no formatting options yields a genuinely surgical edit that touches nothing else, and mem re-renders only its own inserted payload, at the document's own indent unit (tabs included) and line ending. Comments and trailing commas survive in both directions.mem uninstallleft behind the containersmem inithad created -- installing into asettings.jsonwith no hooks at all, which is the common case, createdhooksandhooks.SessionStart; uninstall removed the hook group and stopped, leaving a"hooks": { "SessionStart": [] }husk.tasks.jsonhad the same shape via"inputs": []. Uninstall now prunes an array or object that its own removals emptied. An empty hook array is inert, so pruning one a user happened to have written by hand costs them nothing.mem initwrote LF into files authored with CRLF, andmem uninstallgrew a blank line each time -- the marked block was assembled with hard-coded\n, so installing into a CRLF-authored CLAUDE.md produced a file with mixed line endings, and rewriting the shared block'stools=line dropped the\rfrom it. Uninstall then looked for a literal"\n\n"separator it would never find in such a file and left the surrounding blank lines in place, so each install/uninstall cycle added one. Every write now detects and preserves the file's own line ending, and the separator inserted by install is exactly the one removed by uninstall -- so a file with no trailing newline no longer gains one, and a whitespace-only file is no longer emptied.- The repository's own tracked files can no longer be checked out as CRLF -- a
.gitattributeswith* text=auto eol=lfpins them. Undercore.autocrlf=true, which is the Windows default,docs/integrations/copilot-vscode.mdarrived with CRLF on all 161 lines, and the doc/code consistency test added in 0.2.5 parses that file with a/```json\n/regex that cannot match\r\n. That is a test that fails for a reason having nothing to do with what it tests, on a checkout that is otherwise correct. - The dry run described a shared-block install it was no longer doing -- now that a reinstall refreshes a stale body, a tool already named in
tools=can have work to do, andmem init --dry-runstill reported it as "install would join existing shared block (adds<tool>to tools=)". That case now says it would refresh the body. - The
npm auditnote in CONTRIBUTING.md described advisories that no longer exist -- it documented five dev-only advisories in the esbuild/vite/vitest chain and the reasoning for not forcing the fix. The toolchain upgrade that cleared all five landed before 0.2.5;npm audithas reported zero since, and the paragraph telling contributors otherwise outlived it.
Added
- Nine regression tests that assert on file bytes, not parsed structure -- the formatting damage above was invisible to the existing suite because every assertion went through
JSON.parse, and the round-trip leaks were worse than invisible: two tests had encoded the leftover empty containers as the expected result, one of them inside a test named "uninstall returns the file to its pre-install state". The new tests seed hand-formatted fixtures -- four-space, tab-indented, single-line, comment-carrying, CRLF -- and compare the file to itself after an install/uninstall cycle. Five were verified failing against the previous implementation before the fix landed. - Three regression tests for malformed and stale markers -- an orphaned start marker ahead of a real block, a stray end marker before one, and a shared block whose body was written by an older version. Each was verified failing first.
- A
## Line endingssection in CONTRIBUTING.md -- explains what.gitattributespins, when to rungit add --renormalize ., and the distinction that matters here: files mem edits are the user's, and code that writes them must never hard-code\n.