github vshulcz/deja-vu v0.20.2

latest release: nightly
2 days ago

Added

  • The brief reads the usage log once instead of four times. It printed four figures — today's recalls and bytes, the week's recalls, the week's déjà vu moments, and today's source volume — and called a reader per figure, each of which opened the file, scanned it line by line and JSON-decoded every line including the ones outside its own window. Over a log the size the issue measured on a real machine (3,200 lines, a busy fortnight), the four readers cost 12.2 ms and 76,880 allocations against 3.1 ms and 19,220 for one pass: a benchmark of both shapes ships beside the change. The week note, which a session-start hook writes, went from two passes to one on the same reader. The statusline and the session-start hook had already been folded into it (#2224); this is the rest. Speed is the smaller half: separate passes are separate snapshots, so an event recorded between two of them landed in the second and not the first, and the brief could print a "today" figure and a "this week" figure that were never true at the same moment. TodayDemand, Week, DejaVuWeek and TodayRaw are wrappers around the single pass now, so every other caller keeps its reader and gets the same numbers. (#1576)
  • deja doctor says when the Grok Build plugin is behind. Kimi users have been told since #1721; Grok's plugin is installed from a marketplace entry that pins a commit, so an installed copy stays where the pin left it — the bundle went to 0.2.0 while every installed copy was still 0.1.0, and nothing on the machine said so. The row now reads plugin with "v0.1.0 installed, v0.2.0 ships with this deja — grok plugin update deja", on both the auto-recall and the MCP lines, since the plugin carries both and stands down where the installer already wrote them. Behind, never merely different: grok plugin install ./… installs a working copy that may legitimately be ahead of the bundle, and deja cannot tell one from the other by the files it can see, so a copy at or above this version just reports its number. The installed directory name is generated, so the copy is found by the name in its own manifest rather than by a path; a directory holding another plugin, an unreadable manifest, and a machine with no Grok at all are all silent rather than errors. A test holds the constant and the shipped manifest together, which is the reason TestKimiManifestsAgree exists. (#1828)
  • A password stated in prose is masked. Every rule in the redactor wants a delimiter — a colon, an equals sign, a flag — and a person telling an agent a password writes none, so "also the admin password is hunter2-2026, do not put it in code" went into the index verbatim and deja show and deja ctx read it back in the clear. Found by seeding four obviously fake credentials into a transcript and asking every surface for them: three were masked on the way in — the connection url, the AWS key, the .env assignment and the key named mid-sentence — and the fourth was this shape. What keeps the rule away from prose is the one the international pass already rests on: the value has to carry a digit or a symbol and it stops at the first space, so "the password is wrong" and "the password is the same as staging" are untouched. Index format 49, since redaction runs at ingest and a store built before this keeps the text until it re-reads its sources. (#3729)
  • A mistyped flag gets the same help from eleven more commands. deja search --limt x has named the flag you probably meant since #755 — folding a near miss into the query turned a working search into "you have no such memory" — and the other 22 refusal sites just said no, so someone who typed --limt at deja show had made the same mistake and got less help. index, show, forget, stats, doctor, view, log, remember, restore, promote and handoff now answer through one helper: <command>: unknown flag "--limt" — did you mean --limit?, with the plain refusal when nothing is within a prefix or two edits. Search gains the command name it was the only one to leave out, so two refusals side by side say which parser spoke. The matching rule is unchanged and still deliberately narrow — a bare word is not a flag and a single dash may be a value — so a dashed query term still searches. The suggestion is only as good as the list it is drawn from, and the list sits away from the parser that reads the flags, so a test holds the two together: whatever flag tokens a converted parser mentions, its list has to name. The remaining 11 sites carry their own guidance (sync export names the three flags it takes, blame explains --) and are left alone. (#1829)
  • deja index --quiet for the runs nobody is watching. The command always printed a line and there was no way to ask it not to, so a shell profile or a session hook that keeps the index warm printed deja: index is up to date (…) over the prompt on every new shell — the kind of thing that gets a tool uninstalled rather than reported — and the workaround, deja index >/dev/null, also swallows the errors you would want to see. The flag covers both the incremental run and --rebuild, including the live progress display, since they print through the same sink and a reader would not expect one to stay noisy. Quiet is the success reporting and nothing else: a store that could not be read, an exclude list that is not applied, an index that came out empty for a reason, and any failure to build at all still go to stderr, because a scheduled run that goes quiet about failure is worse than one that prints a line. (#1827)
  • The line-level blame answer says which silence it is. deja blame <path>:<line> printed nothing about the line when it could not answer — a line past the end of the file, a directory, a path that is not there, a file outside a repository, one the version control system does not track, an uncommitted line — so the reader could not tell "this line has no history" from "your :120 was ignored". Each of those now says so in one sentence, with the line count where that is the reason. Found by running the edges against the feature the same day it landed, and it is the rule every other empty answer here already follows. (#3726)
  • A full uninstall takes back deja's own launcher directory. ~/.config/deja/bin stood empty afterwards — the one directory left on a stand where every target had been installed and then removed. #3698's prune walks what the wiring record names, and this directory was never recorded: the launcher writer called MkdirAll straight and the remover took the file without walking up. Recorded before it is made and pruned after the file goes, so the config root comes back holding the record and the lock and nothing else. (#3725)
  • The first-screen suggestion is picked from what people typed as the task. It was a pair of adjacent words from anywhere in any message, and what that finds is a fragment of whatever was being said: on a 2,000-session store the pick was an idiom nobody would search for. Four sources were scored by what a reader running the suggestion gets back and by whether the phrase is one somebody wrote as a task — a bigram from any message, 18 hits and 0 of the top five carrying it in their own title; the subject of the friction line, 66 and 0; the newest session's title, 50 and 1; a phrase recurring across session titles, 50 and 2. Titles win for a structural reason: a title is what somebody typed when they asked for the work. The friction subject scores well and is deliberately not used, because the brief prints that wall one line above the suggestion already. Titles the ingest borrowed from plumbing are skipped — a <teammate-message> envelope becomes harness output: … and no longer looks like an envelope, and on an agent-heavy store that family is most of the titles. The message scan stays as the fallback, so a store whose sessions carry no title of their own still gets a suggestion. (#3714)
  • deja blame <path>:<line> says which session wrote the line. The version control system says who changed it and when; the session that made the change is what says why, and until now the line number was parsed and thrown away. The rule is the one the measurement picked: the session has to have replaced the same text the commit shows as deleted — an edit record holds what an edit replaced, so a match means that session performed this very change. Over 494 commits and 4 dependency bumps from a month of this repository — time-and-file overlap named one session for 378 commits but attributed 2 of the 4 bumps to a session that had nothing to do with them; naming the number the commit closes reached 355 and attributed 2 of 4 as well, because the maintainer's own session names the pull request while merging it; the span rule answered for 116 commits and attributed none of the bumps. Ambiguity is repository-shaped rather than absent: on another repository in the same store 3 of 17 attributed commits had two candidate sessions, and the last edit before the commit wins. So it answers for about a quarter of lines and says which silence the other three quarters are. No "why" line is printed, and that is measured too: over 81 attributed lines, the session's own conclusion overlapped the change 0 times and its first conclusion after the edit 2 times, so any single line lifted out of a session reads as the reason for a change it has nothing to do with — the session, the text it replaced and deja ctx <id> are what deja can stand behind. (#1181)
  • deja blame stops quoting a tool's write confirmation as the reason a file looks the way it does. Measured over 1,439 paths that sessions on a real store actually touched: of the 7,417 snippets it quoted, 1,567 were a tool's own sentence — "The file … has been updated successfully", a patch receipt — and 3,027 of the 4,835 rows carried at least one. The filter already existed and recall has used it since #2068: digest.IsAgentArtifact recognises 1,565 of the 1,567. Tool output only, because a command record starts with $ and is an artifact by that predicate, while what a session ran after touching a file is evidence rather than echo. 540 rows lose their quotes entirely and keep the session, the date and the title. (#3721)
  • deja fix stops answering an error with the command that produced it. A session did re-run what had just failed and the error went away for a reason of its own, so the pair is honest about what happened and useless as a remedy: ran next: X above after this failed: X, on the surface the post-error hook speaks from. The two strings are never equal — a stored command carries its exit status — which is why nothing caught it; compared on the command alone, 1 of the 7 pairs served on a real store that carry a failing command is this shape. Dropped in FixesFor, where all seven callers come through. (#3720)
  • The suggestion on the first screen has to be a phrase that recurs. It was scored on the rarity of two adjacent words and nothing about the pair, so on a store with prose in it the pick was a fragment lifted out of one sentence — a verb and its object, the subject of neither session that held it. Measured on a 2,421-session store: requiring the pair itself in three sessions rather than two moved what a reader running the suggestion gets back from 10 sessions to 18, and 102,558 of the candidate bigrams there appear in a single session against 10,301 in two or more. Two sessions on a store under a hundred, where three of anything is most of the history and the line would go missing instead. What the phrase should be picked from at all is a different question (#3714).
  • The handover names the work rather than the scratch. deja wip's five file slots went to whatever was touched last, and on a real store that was throwaway paths — a probe script under a temporary directory, the harness's own scratchpad, a log — in 238 of the 400 most recent sessions with file records, with a repository file that had lost its slot to one in 29 of them. The same paths deja files already refuses are refused here, by shape rather than by asking the disk, since this is a leaf package: 189 of the 238 clear it, 180 handovers stop naming files at all because they touched nothing else, and the line names a repository file in 171 sessions against 162 before. What the compaction handover names is what the next turn opens. (#3716)
  • deja wip names files rather than words. Every whitespace-separated token of a files or edit record counted as a path, so a record carrying a sentence contributed one "file" per word — files in flight: …/scope_audit.sh, was., it, as, exactly — and the words crowded the real paths out of the handful that get named. This is the line an agent reads to pick its own work back up after a compaction. A token has to look like a path now: a separator, an extension, or a leading /, ~ or .. (#3706)
  • deja files answers from the project it was asked in too. It read the machine and ranked by how much a file was touched, so a generic topic answered with the busiest project's tree: asked inside this repository, deja files "timeout" named eight paths — SQL migrations and a Python service from two unrelated projects — and not one of them was here, while the same question scoped to this repository named its four. An agent cannot tell the difference by reading them, which is what makes it worse than no answer. Same default, flags and widening as deja how, the scope named in the text and carried as project in --json, and the project filter is a set now, so a worktree that records its own name is still the project it belongs to. (#3713)
  • deja how answers from the project it was asked in. It read the whole machine and ranked by how often a command had run anywhere, so the busiest repository answered every question asked in any other: inside this one, deja how "test" returned rtk go test ./... — another project's wrapper, 40 runs — three times before this repository's own command, which was not in the eight at all. The scope is now the project of the working directory, derived the way deja wip and the hooks derive it (worktrees included); --project still asks about another one, --all-projects asks the machine, the text says which of the two answered, and --json carries it as project. A project with nothing to say still gets the machine's answer rather than "no command on this machine mentions it" — an agent told nothing exists invents one. The MCP how tool takes the same default. (#3705)
  • The asked-twice line stops telling a reader their own resumed conversation back to them. A resume, a fork or a share writes the prior turns into a second session file, so every question in it repeats verbatim under a second key — and on a 2,300-session store with a year of history, all nine repeats at least 48 hours apart were that: the winner was one opencode conversation under four ids, the same eight asked hashes in each, "asked in four sessions" over four weeks. The signal was already in the manifest, so the test costs no record read: the two sessions' asked hashes minus the one they matched on, a third of the rest in common. Two sessions that share only the question are the case the line exists for and still fire, which is why the matched hash is excluded rather than counted. That store's line now says nothing, which is the honest answer for it. (#3711)
  • recall_context resolves an id as an id. Every session-start block prints a session id and tells the agent to follow that up with this tool — and an id is also a searchable string, so any transcript that mentions one matched it lexically and the search answered first. Measured on a real store by asking for the 120 most recent sessions by their own id prefix: 24 brought the right session back and the other 96 answered with a different session, stated with the same confidence. Now 120 of 120. recall takes the same rule, since it is the tool an agent reaches for first and it was worse: asked for those same ids it answered about other sessions 77 times, named the right session 37 and said nothing 6. Id-shaped rather than merely wordless — a digit, a dash or an underscore, which every store's id has and a word does not — so "timeout" and "wiring" stay searches. The block's instruction names the id too, since the other half of the same measurement is that a single distinctive word from the quoted line brings its session back at rank 1 in 3% of cases and three of them in 39%. (#3717)
  • Copilot Chat's second store is read, and on a newer extension it is the only one with the history in it: workspaceStorage/<hash>/GitHub.copilot-chat/transcripts/<id>.jsonl. On VS Code Server 1.137 there is no chatSessions directory at all — the reporter's machine had 47 transcripts holding eleven weeks of chats and deja found zero files — and on desktop 1.136.1 both layouts sit side by side, 28 chatSessions directories and five transcripts. The records are a type-discriminated event log rather than the kind 0/1/2 delta log beside it, so it is its own reader: user turns from user.message, replies from assistant.message, the files and commands a tool call names, and the project from the workspace.json two levels up or from the paths the session touched. A transcript with no user turn at all is an agent run and is indexed — 11 of those 47 and all five here have none, and what the agent said is the only record of that work. Measured on this machine: copilot-chat 17 sessions → 22, and the second probe per workspace directory costs about 20 ms of discovery across 179 of them. Reported with the format, the histograms and both stores' contents by yizhixiaokong. (#3637)
  • An install and an uninstall give a reader's AGENTS.md back exactly as it was. Grok Build's guidance is a marked block inside a file its owner writes, and the round trip left one blank line behind: install trims the trailing newlines and appends \n\n before its block, and the uninstall cut the block alone. 56 bytes in, 57 out — a one-line diff in somebody's dotfiles, from the same pair #2606 fixed on the goose side from the other direction. The trailing run of blank lines still collapses to one at install time, which is the file's state while deja is wired and is now stated in a test rather than left to be found. (#3703)
  • The manual kilocode, gjc and Command Code get declares the name of the directory it sits in. All three had it under skills/deja-search/ — the CLI skill's name — while its frontmatter said name: deja-history, and a skill whose name and directory disagree is one a loader may drop or rename: Claude Code requires the match, and Gemini renaming the loser of a collision is how #3665 was found. It goes under skills/deja-history/ now, the copy deja wrote under the old name comes out on install rather than outliving the fix, and the CLI skill that legitimately lives in a deja-search directory is left alone. Found by reading every file deja writes the way a harness reads it, which is now a test: every SKILL.md must declare the name of its own directory. (#3700)
  • deja uninstall takes back the directories it made. On a bare home a full install and a full uninstall left 33 of them standing empty, from the harness roots down to a plugin directory four levels deep — ~/.pi/agent/extensions, ~/.openclaw/hooks, ~/Library/Application Support/Code/User/prompts, ~/.agents/skills. Two halves: the record kept only the file's own parent, so a writer that creates a tree left everything above the last level unvouched for and the prune from #3239 stopped at the first directory it could not speak for; and fourteen writers removed their own directory and never looked up. Every level a write creates is recorded now, the prune walks upward while each is empty and deja's, and OpenClaw's pair stops reporting removed for a directory that was not there. Thirty-three became zero, held by the same walk the files have: after a full round, no directory the record names may still be there. (#3698)
  • A config with a UTF-8 byte order mark installs. PowerShell 5.1 writes one by default — Set-Content, Out-File, a > redirect — and editors on Windows may too, and every JSON target refused such a file with invalid character '<mark>' looking for beginning of value: a remedy naming a character nobody can see, in a file its owner did not knowingly change, and one a harness may well read, since VS Code's own readers strip a mark. It belongs with the line endings and the indent — read past it, write it back — so the file keeps the shape its owner's tools give it, and a file that had none does not get one. (#3696)
  • deja doctor stops naming a doubled hook that an install has since collapsed. The repeated-injection row reads what actually happened rather than what a config says, which is what made it the check that found #3421 — and it read the whole usage log, so one bad morning was reported for as long as the log was kept, with a remedy the reader had already applied. Two bounds now, whichever is later: the last time deja wrote its wiring record, since an install is what collapses the entries, and a week, so a doubling somebody fixed by hand stops being named once no new event follows it. (#3697)
  • ZCode's writer reads its own hook entries whatever the build is called. #3681 gave six writers that predicate and this one kept the narrow name test, so on Windows — where the test binary is deja.test.exe — its uninstall read deja's own entries as a stranger's and left the whole block behind. One invariant now asks every auto target the same question, in the shape the machine in #3681 was in: every entry naming a build that is not the one installing.
  • The wiring record holds only targets deja knows. It is written from the arguments, before any of them has been through the installer, so deja install nosuchtarget — a typo, or a script listing the names — was kept beside the targets that worked, and every repair after an upgrade retried it, failed, and counted it in the "could not rewire" line at session start. A name already in a record written by an older build drops out on the next install. (#3695)
  • Hook lines survive a home directory with a space in its name. The launcher lives under the home directory and a hook entry is a command string the harness hands to a shell, so on such a machine every one of them was unrunnable — 34 lines on one stand, and sh says it plainly: sh: /tmp/deja: No such file or directory. Auto-recall was dead across twenty harnesses with nothing reported, because doctor skips any path with a space on purpose — the right call for reading somebody else's file and the wrong one for a file deja wrote. Windows is where an ordinary machine meets this: C:\Users\Name Surname, C:\Program Files. deja quotes its own hook lines where a shell needs it, in one helper rather than at twenty-five call sites, with cmd.exe's quoting on Windows and the shell's elsewhere; an ordinary path is left unquoted. An entry already written in the broken form is recognised as deja's own and rewritten rather than read as somebody's wrapper, so an install repairs a machine that already has it, and doctor can now read a quoted path and report a binary that is gone. The invariant it was found with ships too: install every auto target into a home with a space in its name, and every command line must name a file that is there. (#3692)
  • One install at a time. Every writer reads a config, edits it and writes it back, and nothing stopped a second deja landing in the middle: deja install claude-auto and deja install statusline started together lost one of the two wirings in eight runs out of twelve, both of them editing ~/.claude/settings.json. Nothing is corrupt — the write renames into place — the later process simply built its content on the file as it was before the earlier one. The pairing is what a machine does on its own: the repair after an upgrade runs an install from the hook path, so a session starting while someone types deja install is this race. An advisory lock beside the wiring record now serialises the two verbs, and the repair stands down rather than waiting, since whoever holds the lock is writing the same wiring. A machine that will not give deja a lock carries on without one. (#3691)
  • ZCode's uninstall takes back the containers it added. Its two writers created mcp.servers and hooks without recording them, so a full uninstall left {"hooks":{"enabled":true},"mcp":{"servers":{}}} in a file deja had created itself. A container the reader already had is still theirs, switch included — that is gemini's rule and the reason for it holds wherever the block is not deja's. (#3690)
  • deja install --all keeps the auto layer it finds instead of turning it off. DeepSeek Harness keeps the server and the plugins in one patch list, so its plain target drops the auto row and deletes the plugin — that is how someone steps back from --auto, and it is what --all did to them unasked, reporting deepseek: updated. --all now refreshes the -auto sibling of any target the record says is wired, after the plain one rather than instead of it. Found by installing every target twice and asking which files changed on the second run. (#3687)
  • One text in the shared skill file. ~/.agents/skills/deja-history/SKILL.md is read by sixteen harnesses, and pi carried a body of its own from before the current one existed — two tools where the shared text describes six, nothing about reading a result marker, nothing about the bounded result window. pi joined that directory in #3657, and from then on deja install pi replaced what codex, omp, Senpi, Kimchi, gjc and Zed are told, while the next install of any of those put it back: on a machine with both, ordering decided what every agent read. (#3688)
  • A repeat deja install goose changes nothing and says so. Goose keeps the slash command and the MCP extension in one config.yaml; each writer removes its own block and adds it back, and removing the only entry takes the key with it, so each re-appended at the bottom under the other's — the two keys swapped places on every install and the report said "updated" twice with nothing to point at. When the entry deja wants is already in the file, exactly, the file is left where it stands. The repair after an upgrade runs that install, so a config.yaml kept in a dotfiles repository showed a diff every time deja moved. (#3689)
  • The status bar is part of deja's own bookkeeping, which fixes five things at once. recordWiring skipped the statusline target, and with no other target the record was never written at all — so nothing knew deja had wired it. The status bar is also the one wiring that names the binary directly rather than the launcher, so a build that moves leaves it running a file that is not there, on a one-second timer: the repair never reached it, deja install statusline called deja's own stale entry the reader's and offered to combine the missing binary with the present one, deja uninstall statusline would not remove it either, uninstall --all walked past it because a status bar is not a harness, and the uninstall left behind both the settings file it had created and a .bak of deja's own wiring it reported as "configs you already had". The entry is now recognised by the pair every hook is recognised by — a deja-named binary running one of deja's subcommands — which is also what mentionsDeja was missing. A statusline the reader combined with their own stays theirs, untouched by either verb. (#3684)
  • uninstall takes back the commands directory it created, and puts back a /deja command it replaced. Claude Code's slash command has a writer of its own and it kept neither rule the other command files have had since #2581 and #2600: it made ~/.claude/commands without recording it, so an empty directory outlived every uninstall, and it removed its file without restoring the one install had replaced — a reader with their own /deja got it back as a .bak nobody restores. Found by walking every file in a home after install and after uninstall rather than by checking the paths a test names. (#3685)
  • deja install takes several targets, because deja doctor asks for exactly that. The stale-wiring row — the one a person reads when the binary has moved and the repair on the hook path cannot run — prints deja install followed by every target in the record, and on any machine with two of them that line answered "install needs a target". --all and --auto still stand alone. (#3686)
  • Cursor's /deja carries its description again. Cursor reads a command's description from the first line of the file and does not parse markdown frontmatter, so deja's entry showed --- in the one list where a person picks which command to run — while the two skills beside it described themselves. Measured with two probe commands in one palette, one of each shape, at both user and project scope. The harnesses whose own docs specify frontmatter keep it. (#3666)
  • Gemini gets no /deja command file, because its skills are commands. The rename in #3655 moved the collision rather than ending it, and Gemini said so on its own start screen: "Skill command '/deja-search' was renamed to '/deja-search1'". Everything deja installs lands in one flat namespace there — the MCP server's prompt is /deja, the CLI skill is /deja-search, the manual is /deja-history — so a file beside them is a third entry doing what the other two already do, whatever it is called. Gemini joins the eight harnesses where the skill is the command, and both names the file used to have are taken out on install so the clash does not outlive the fix. A command of the reader's own under either name is left alone. (#3665)
  • The Commands section says what it means where deja writes no command file. Ten harnesses make a skill invocable by name, so the skill deja installs is the command there and a file beside it would only add a second entry — Gemini proved that by renaming one of the two. Those rows were simply absent, and an absent row reads as "deja has no command here" when the truth is that it is installed under another name. They are rows with the state skill now, naming the skill file, and a line under the section says each harness spells the invocation its own way (codex /skills, Kimi /skill:<name>, Copilot /<skill-name>) rather than promising one. The list of those harnesses moved into the product, beside the report that prints it, so the capability test and the report read the same one. (#3667)
  • deja doctor stops counting qwen's and OpenClaw's own state as transcripts it failed to read. On the machine this was found the rows said 11 not recognised here for qwen and 1 for OpenClaw while every session in both stores was indexed — and eleven is a number a reader takes as a parser that cannot cope with their history. What it counted was qwen's <id>.runtime.json, meta.json and extract-cursor.json, and OpenClaw's agent/models.json: bookkeeping, not conversations. Same call as Continue's sessions.json and Kimi's state.json. That machine now reports zero unread files across all 33 harnesses, down from twelve. (#3676)
  • Hermes's published store paths name the single-file layout as well. ~/.hermes/state.db is what a machine with one profile has — the reader has taken it since 0.17 and the registry page says so — but the machine-readable store_paths listed only the per-profile form, so a reader checking the published list against their own disk found nothing. Caught by walking every documented path on a real machine: of the 33, this was the one that matched no file it should have. (#3680)
  • The documented path for Kimchi's store was one no machine has. Its own binary builds <agent>/sessions/--<encoded cwd>--/<id>.jsonl in getDefaultSessionDirPath — a directory per project, the way pi and Senpi do it — while the registry said the root was flat and the reader's comment said so too. Measured rather than read: a session placed flat is rejected by the client (No session found matching …, because its own lookup enumerates the per-project directories), and the same file under the encoded directory opens. deja's walk is recursive so nothing was lost, but the published path, the fixture and the comment were all describing the wrong layout — which is the note that invites someone to narrow the walk later. Fixed, with the fixture moved and a test reading it from there, plus one that keeps a flat file working. (#3678)
  • Every generated hook and plugin names the launcher, not the binary the install ran from. The launcher exists so a config never has to name a build (#3422) and the config writers have used it since; the writers that generate a plugin or an extension file kept baking the absolute path. On the machine this was found, eleven auto wirings — opencode, kimi, antigravity, pi, senpi, hermes, OpenClaw, cline, omp, DeepSeek Harness, Goose — ran hooks through builds in a scratch directory, and the day that directory is cleaned auto-recall stops there with nothing said. Nine writers changed; Goose was already going through it under the other helper's name, which a first pass missed and double-wrapped, pointing the launcher at itself. One test over every auto target now holds the invariant: install with a distinctive path, and no line running a hook- subcommand may carry it. Per hook line rather than per file, because qwen, crush and ZCode keep the MCP server in the same file and that entry names the binary deliberately. deja install <harness>-auto rewrites a file that already carries a baked path. (#3682)
  • A hook entry from a build under another name is deja's own, so an install collapses it instead of stacking beside it. Reported as "the hook seems to fire several times", and it did: ~/.claude/settings.json on the machine this was found carried six deja entries per event, one per throwaway build that had ever run an install — six processes per prompt and the recall block arriving several times in a turn. The same count elsewhere on that machine: codex 30 deja commands, cursor 35, qwen 32, grok 68. An entry was recognised as deja's own by the exact command string, by the basename deja, or by a path in a record capped at ten — and a build called deja-cont matches none of those, so every install read the old lines as somebody else's and added its own. It is the pair that identifies them now: a binary named like a deja build running one of deja's own hook subcommands. A wrapper line that merely contains the hook is still left alone, and a foreign other hook-prompt stays foreign. Six writers share the predicate, so one change collapses all of them — measured on copies of that machine's files: claude 6 → 1 per event, codex 30 → 5, cursor 35 → 5, qwen 32 → 4, grok 68 → 4. deja doctor's repeat note was silent for the same reason and now fires. (#3681)
  • The two rows with a database half say so when sqlite3 is missing. Kilo Code and ZCode each keep part of their history in SQLite and named the file without naming the tool that reads it, so on a machine without the CLI those sessions were absent from recall while the row reported the store present. Every store that is only a database already said it; these two have transcripts as well and took a different path through the report. (#3679)
  • ZCode's CLI database is read. It was left out because nothing said what shape it was in, and a reader built on a guess loses the half of a store it does not understand in silence. The shape is now attested by zcode-stats 0.8.0, a read-only dashboard over the live database: it names ~/.zcode/cli/db/db.sqlite and queries session(directory, task_type, …), message(id, session_id, data) with json_extract(data,'$.role'), and part(data) with json_extract(data,'$.type'). That is OpenCode's schema, which deja already parses for OpenCode and for Kilo's CLI — so this is one more root for an existing reader, with DEJA_ZCODE_DB to move it and sqlite3 as the prereq for that half. The entry says what this rests on: a third party's attestation, not a running ZCode checked here. (#3675)
  • The Cherry Studio import file is pinned against the app's own validator, read out of the installed bundle rather than from its repository: object({ mcpServers: record(string(), McpServerConfigSchema) }), where the server schema is .strict() and type is one of four literals. What deja writes validates — and the strictness is the reason to pin it, because one key the app does not know fails a paste whole instead of being ignored. The server also arrives named deja, since the importer takes the name from the key. (#3674)
  • deja resume prints kilo -s <id> for a Kilo CLI session instead of refusing it. The rule told the extension's tasks from the CLI's sessions by comparing the session's path with the database file — and a session read out of that database carries the directory it ran in, not the file, so every CLI session was refused as an editor task. The one half kilo -s exists for. It tells them apart by the task path now and runs the command in the session's own directory, the way the opencode case does with the same field. Verified by running what deja prints: Kilo reopens the session and its screen shows the original turn. (#3677)
  • deja install kilocode wires Kilo's CLI, which is a different program from its editor extension and has its own config. Kilo vendors OpenCode, so <config>/kilo/kilo.jsonc takes OpenCode's mcp block; deja wrote only the extension's settings, so on a CLI-only machine the install said "only the skill was written" and kilo mcp list said "No MCP servers configured". Both are written now — kilo mcp list prints ✓ deja connected — the note names what was skipped rather than what was not, and the doctor row points at whichever of the two configs is on the machine. Verified on @kilocode/cli 7.7.3, along with the CLI store: a real kilo run session landed in ~/.local/share/kilo/kilo.db, deja indexed it and recall returned the typed phrase. (#3672)
  • Senpi is wired, not just read, and the five capabilities the registry had as unknown are answered. They were unknown for one reason — nobody had found a package for it — and there is one: installing @code-yeongyu/senpi answers every surface on senpi's own screen. The server goes in <agent>/mcp.json (its palette lists mcp:deja:deja), the shared skill is what it reads (with both copies present it prints "deja-history" collision: ✓ <agent>/skills ✗ ~/.agents/skills (skipped), so the local one is retired on install), pi's extension loads unchanged and brings the /deja command, and a session started with it records what deja hook-context returned as {"type":"custom_message","customType":"deja-recall"} — auto-recall arriving, in senpi's own transcript. deja resume prints senpi --session <id> and deja handoff --to senpi runs. One thing to know before wiring both clients: senpi's first run moves ~/.pi/agent to ~/.senpi/agent, sessions and config and extensions together, so pi is left with an empty directory. (#3670)
  • deja doctor stops counting an extension's own state as transcripts it failed to read. Senpi's terminal extension writes one file per session under sessions/<project>/extensions/terminal/, and the row said "2 not recognised here" about a store whose every transcript had just been indexed — while doctor --json for the same store said ok with no unread count at all. Anything under an extensions directory inside a session root is the harness's state, not a conversation; the same call Continue's sessions.json and Kimi's state.json already get. Reachable for every pi descendant. (#3669)
  • deja doctor says whether /deja is installed. MCP wiring has a section and hooks have one; the command — the third thing an install writes, and the one that makes deja discoverable when someone types a slash — had none, which is why Gemini renaming it to /user.deja had to be found on Gemini's own screen rather than by any check deja ships. Eleven rows, name and state and path, and the path is worth reading: two harnesses keep the command under a different name. someone else's is its own state, separate from missing, because only that one is a reason to leave a file alone. Also a commands array in doctor --json, from the same table, so the two surfaces cannot disagree. (#3664)
  • Command Code is wired, all four of its surfaces: the server in ~/.commandcode/mcp.json, a skill, a /deja command, and hooks under a hooks key in settings.jsondeja install commandcode-auto for the last. Two details there would fail silently if copied from another harness and are pinned by tests: its timeout unit is seconds, so the milliseconds deja passes elsewhere would read as ten minutes, past its documented maximum; and its tool matcher is a regex over its own display names (SHELL, EDIT, WRITE), so a Claude-shaped Bash matcher never fires. There is no per-prompt event — the four are SessionStart, PreToolUse, PostToolUse and Stop — so the digest rides SessionStart. The CLI is closed and none of this is verified here: every path comes from two independent integrations that cite the vendor's docs, and the registry entry says exactly that. (#3651)
  • deja install kiro writes Kiro's global steering file, which is its user-level guidance channel: ~/.kiro/steering/deja.md, scanned for every project. Four lines, and the length is the point — a steering document declares an inclusion mode and the only one measured as actually loaded by kiro-cli is always, so this text is in front of every turn whether it is wanted or not. manual is not loaded and cannot be invoked from a session, which is also why the registry records Kiro's skill as impossible rather than as work: an on-demand skill is not something Kiro has. (#3651)
  • deja resume prints kilo -s <id> for a Kilo Code session that came from its CLI store — the flag is "session id to continue" in Kilo's own CLI options. An editor task is refused with the reason instead: those live under the host's globalStorage and reopen from Kilo's history view, which is the same split Roo has and the reader already tells apart by path. (#3651)
  • Cherry Studio gets the skill, and the gap that said it had no channel for one was wrong: it discovers the skill directories of the agent CLIs a machine has — ~/.agents/skills among them, which is deja's own shared channel — and lists what it finds for the user to enable per agent. deja install cherrystudio writes the file there, so the app lists it; enabling it for the agent is the click left, and the install note says so. (#3651)
  • deja resume works for Continue, and the registry entry that said it could not was wrong about Continue's own CLI: cn --fork <sessionId> loads any session in the store deja reads — its history manager opens <sessions>/<id>.json — rather than only the last one. The flag forks, so the history comes back under a new id instead of continuing the old one, and that is printed on stderr beside the command rather than left for the user to discover. (#3651)
  • deja resume prints a real command for Kiro, Kimchi and gajae-code instead of handing over a paste. Both were recorded as unverified; both are settled from their own sources — Kimchi's argument parser rewrites --resume <selector> to --session <id>, and gjc's session-operations document has --resume <id|path> opening an existing session, forking it into the current project when it belongs to another, which is why deja prints no working directory there. Kiro takes kiro-cli chat --resume-id on 2.2.0 and newer, and a Kiro IDE session — recognisable by its sess_ id — is refused with the reason, since those reopen from the app. (#3651)
  • Kilo Code gets a /deja command. The registry had it down as work nobody had found the surface for, and Kilo's own workflows doc names it: global slash commands are markdown files in ~/.config/kilo/commands/, so a file called deja.md there is invoked as /deja. (#3651)
  • VS Code Copilot Chat gets a /deja command, which is the only surface it has: it fires no session-start and no per-prompt hook, so before this the tool was there and nothing told the model to reach for it. Its commands are prompt files, so deja install vscode now writes <User>/prompts/deja.prompt.md for every host it finds — the same User directory the reader already walks — with the description the chat box lists, an argument hint, and the CLI as the fallback for a window where the tool is not connected. (#3651)
  • ZCode gets both halves: deja install zcode writes the server into mcp.servers in ~/.zcode/cli/config.json, and deja install zcode-auto adds hooks on SessionStart and UserPromptSubmit to the same file — so recall arrives without being asked for. Three details decide whether that works and every one of them fails silently: config-file hooks do nothing without hooks.enabled, a config hook gets no template expansion so the command needs an absolute path, and the output schema is strict — one unrecognised key and the whole response is discarded. That last one is what the new --strict flag on deja hook-context and deja hook-prompt is for: it drops deja's receipt line, which no other host minds, and keeps the context. Shapes from the memory plugin that inspected a live ZCode and shipped an installer against it, not from ZCode's own docs, which do not describe them. (#3651)
  • Three guide pages for the harnesses that just landed: Kiro, Kilo Code and Cherry Studio. Each answers the question people actually type — where that tool keeps its history, whether it remembers anything between sessions, how recall gets in — and each carries the one detail its store has that a reader would trip over: Kiro's reply arriving in pieces under one message id, Kilo's two stores and the migration between them, Cherry Studio's snapshot per stream chunk and the import its MCP list needs. Twenty-two per-agent pages became twenty-five. (#3103, #3644)
  • deja install kiro, deja install kimchi and deja install gjc wire three of the six stores that landed read-only, each path taken from that tool's own source rather than from the lineage it belongs to. Kiro's server goes in ~/.kiro/settings/mcp.json, which both its CLI and its IDE read, with a note about custom agents not inheriting global servers. Kimchi's goes in <agent dir>/mcp.json, honouring KIMCHI_CODING_AGENT_DIR the way the reader does. gjc gets the server and its native skill directory — the one gjc actually loads, since Claude's and Codex's are import candidates there. What is still somebody else's switch is recorded as such: Kimchi runs deja's Claude hooks and skill only after two compatibility extensions are enabled by hand, and gjc's hooks are TypeScript modules with pi's event names, which is a port rather than a config line. (#3651)
  • deja install kilocode wires Kilo Code rather than only reading it: the MCP server into <globalStorage>/kilocode.kilo-code/settings/mcp_settings.json for every host that carries the extension — the file Kilo's own KilocodePaths names — and the shared manual into ~/.kilocode/skills/deja-search/SKILL.md, where its loader looks first. A machine with the CLI and no editor gets the skill and a note saying the server was not wired anywhere. Hooks it does not have, so auto-recall stays a gap pointing at the upstream pull request rather than a promise. (#3643)
  • deja install cherrystudio hands the app a server it can import. Cherry Studio keeps its MCP servers in its own SQLite store — a drizzle schema seeded from a built-in preset list — so there is no config file to write, and writing into a running app's database is not an installer's job. What it has is Settings → MCP → Import from JSON, so deja writes <config>/deja/cherrystudio-mcp.json and says where to point it, the way the aider target writes a file the tool will not fetch itself. The note rides on a second run too: the file being unchanged does not mean anyone has imported it. Hooks and slash commands the app has no third-party surface for, and the registry records that as impossible rather than as work someone could pick up. (#3644)
  • Five more stores are read, and none of them needed a new format. Senpi and Kimchi Coding are pi descendants that kept its envelope, so both are a root and a name; Command Code and ZCode write a flat role/content/timestamp transcript under a Claude-shaped project directory, which is one small reader for the two. Two details are worth the words: Command Code keeps <session>.checkpoints.jsonl beside each transcript, a snapshot stream that read as a conversation adds a session with no words in it, so it is skipped by name; and a line whose role is neither user nor assistant is tool output, which is where a command's error text lives and where a user's next search for it starts. ZCode's SQLite store stays unread until a sample of that schema exists — a reader built on a guess drops the half of a store it does not understand without saying so. gajae-code is a fifth of the same kind, and it keeps its sub-agent passes one directory below the session they belong to — indexed as sessions of their own they repeat the parent's work and compete with it for the same recall slot, so they are skipped unless DEJA_INCLUDE_SUBAGENTS=1 asks for them, the switch Claude Code's and Cursor's sub-agents already use. Shapes read from tokscale's readers, which is the only place they are written down. (#3647)
  • Kiro is read, both of its clients. The CLI writes a header and a transcript per session under ~/.kiro/sessions/cli, and its records carry the text in data.content[].data with the time in seconds — with one thing worth knowing: several AssistantMessage records can share a data.message_id, because the client appends the answer as it streams and each record holds the next piece rather than the whole answer so far, so a run under one id is joined and a recall quotes a sentence instead of a third of one. The IDE — Kiro is a VS Code fork — writes a directory per session under the workspace instead, session.json beside messages.jsonl, in a payload shape current builds use and the flat role/content shape they used before; both are read. Its own bookkeeping records are not turns and are dropped rather than attributed to a role. Read support only for now, and two stores are deliberately left unread: the IDE's globalStorage mirror and the TUI's conversations_v2 SQLite, where no sample is in hand and a reader built on a guess is one that loses history quietly. (#3103)
  • Cherry Studio is read. It runs Claude Code sessions from a desktop app and writes them as ordinary Claude Code transcripts under its own app data, so the parsing is Claude's — with one difference that matters for text rather than for tokens: Cherry Studio appends the same API call three or four times as the response streams, a new uuid each time and the text growing. Read plainly that is one reply stored three times in prefixes, so a recall could quote half a sentence and deja show print the answer twice before finishing it; the reader collapses a run by its request id and keeps the longest. Stock Claude Code writes one record per turn and its reader is unchanged, which a test pins. 51,851 stars, and the store was unread. (#3644)
  • Kilo Code is read, both of its stores, and neither needed a new parser. The extension (kilocode.kilo-code) writes Roo's task shape under the host's globalStorage, and the CLI writes OpenCode's message schema to ~/.local/share/kilo/kilo.db — Kilo is a Roo fork that vendors OpenCode, and its own legacy-migration reads the task directory to import it into that database, so the task files are the history of anyone who used it before the migration and the database is where it goes afterwards. 27,321 stars and deja read neither. Read support only for now: nothing is wired into Kilo, which the registry records as a gap rather than leaving blank. (#3643)

Fixed

  • deja doctor can read the server entry under Zed's own key. Its settings name the server deja-context-server — the id the extension owns — in a context_servers map, in a file with comments in it, so the JSON walk never ran, the container key was one nobody had added, and the fallback looked for a key called exactly deja. The row said wired while the server pointed at a build in a scratch directory. A quoted key whose name starts with deja now counts as deja's own in the files read as text, context_servers joins the containers the JSON branch walks, and a file that parses but holds nothing of deja's under any known container falls through to the text reader rather than answering "nothing". (#3683)
  • deja doctor can name the binary codex runs. The keyed read walked forward from the anchor and stopped at the first sibling that was not command, which describes a YAML mapping and not a TOML table: every key of a table sits at the header's own indent, so the scan broke on the line codex writes right after [mcp_servers.deja]type = "stdio". A config whose command was not the table's first key therefore read as naming no binary, and neither the gone-binary check nor the temporary-directory one could fire. Found by codex mcp list naming a build in a scratch directory while the row said wired. (#3668)
  • deja doctor can name the binary OpenClaw and ZCode run. Both keep their servers one level deeper than everyone else, under mcp.servers, and that map was read as though it were a single entry — no command in it, so the walk returned nothing and the file parsed as JSON, which meant the text fallback never ran either. Neither the "the binary is gone" check nor the "the binary is in a temporary directory" one could fire for those two; on the machine this was found, OpenClaw's entry ran a build under a scratch directory and the row said wired. (#3663)
  • deja doctor reads the binary a harness is wired to from the entry deja wrote, not from the first command anywhere in the file. goose keeps its slash_commands in the same config.yaml as its MCP extensions and one of those commands is called deja, so the whole-file scan answered with that name — a bare word, which is on PATH and is not an absolute path, so both binary checks had nothing to look at — while the extension three lines from the top ran a build left in a scratch directory. The attributed read goes first now. The class is wider than goose: any config that names deja twice, once as a server and once as a command or a hook, could answer with the wrong half. (#3662)
  • deja doctor reports the MCP wiring of all nine harnesses it never named: DeepSeek Harness, Roo, Kilo Code, Kiro, Kimchi, gajae-code, ZCode, Command Code and Cherry Studio. The table those rows come from also carries every per-row check — duplicate registrations, a binary that is gone, a binary in a directory something else will delete — so for those nine the report had no state, no path and no check. On the machine this was found every row that had a check was repaired and dsh was still running a build from a scratch directory, with Roo pointing at another one. Two readers had to learn a shape for the rows to mean anything: dsh has no server key at all — serverName: deja is a field in a patch-list row with command beside it, not below — and four harnesses' manuals are written by their own install target rather than by the generic guidance step, so that column said unsupported about files deja had written. (#3661)
  • The wiring repair no longer adopts a build in a directory something else will delete. It stood down for the machine's TMPDIR and nothing else, so a scratch build under ~/.claude/jobs/<id>/tmp/ counted as "deja moved" and rewrote every recorded target to itself — on the machine this was found, 28 of them, which is why repairs made by hand kept coming back stale. The rule is now one directory called tmp or temp anywhere in the path, shared with the check that names a disposable binary in deja doctor. The only screen that ever said this was happening was Gemini's own start banner, quoting deja's note back. (#3656)
  • deja doctor stops calling Zed wired when nothing can start. Zed takes the server in two shapes under one id: the entry deja install zed writes, which names the binary, and the entry Zed writes when an extension provides it, which names nothing and defers to extensions/installed/deja-context-server. On the machine this was found, that extension was a symlink into a scratch directory that no longer existed — an enabled server with no executable path anywhere — and the row said wired because the id was in the file. It now says the entry expects an extension that is not installed — and deja install zed repairs that entry instead of reporting "unchanged", which is what it did before: refusing to touch an extension-shaped entry is right while the extension is there and wrong once it is gone, and the remedy the report named did nothing. (#3660)
  • An MCP entry that runs a deja build under another name is visible again. go build -o /tmp/deja-probe and an install from it leave an entry no later deja recognised: the name test knows deja, deja.exe and deja-hook, so deja-cont matched nothing, both binary checks stayed silent, and the row said wired because the server key was right. The key is the claim now — an entry under the deja key is deja's whatever binary it names — read for JSON, and for the YAML and TOML spellings the formats that are parsed as text use. Found on a machine whose Hermes server pointed at exactly such a build. (#3659)
  • /deja works in Gemini CLI again. Its command namespace is flat and it lists the MCP server's own prompt beside the command files, and deja named both of them deja — so Gemini renamed both, to /user.deja and /deja.deja, and the name the install receipt tells people to type belonged to nothing. The file is deja-search.toml now, invoked as /deja-search, and the colliding one an earlier version wrote is removed on install. Found by taking a screen of Gemini's own first page: gemini mcp list reports the server as connected either way. (#3655)
  • deja doctor stops calling codex's hooks wired when codex will not run them. Codex pins trust per hook, not per file, so a machine that approved deja's hook when there was one sits with four unapproved — its own first screen says "5 hooks are new or changed… Continue without trusting (hooks won't run)" while the row said wired, because the row was about the file. Measured here: five events in hooks.json, one pin in config.toml. The row now says 1 of 5 hooks approved and how to fix it. (#3654)
  • pi no longer announces a deja skill collision on every start. deja was writing the same skill twice into one host — pi's own directory and the shared ~/.agents/skills, which pi also scans — so pi loaded one, skipped the other and printed the conflict every time. One file now, and the old copy goes with the install. (#3657)
  • deja doctor says when a harness is wired to a deja that is neither this binary nor the one on PATH. On the machine this was found, grok's server pointed at a build left behind by a probe run in a scratch directory: it works until that file goes, and the row read wired because the config file was fine. The note names the path and the install target; deja's own launcher shim and the PATH binary are not strangers, which is what keeps it quiet on an ordinary machine. The MCP rows carry it too, and that is where it earned its keep: five harnesses on that machine — grok, gemini, antigravity, goose and omp — were pointing at builds left behind by probe runs, every one of them reported wired. (#3656)
  • The two remaining git budgets on the hook path get the same treatment windows already got for the root lookup. The compaction fingerprint runs seven git calls under one deadline, and when it expires the result is not an error a user sees but the opposite: the fingerprint comes back partial: and tells the agent not to trust the context deja has just handed it — on the runner one call spent all 750 ms on a repository with a single dirty file. The file-ranking probe has the same shape, and what it costs when it misses is the ranking itself: recall falls back to recency silently. Both are platform budgets now, and both have a test on the number the product ships rather than on a number the test widened to keep itself quiet. (#3645)
  • goose sessions on macOS are read. goose resolves its own directories through etcetera's choose_app_strategy with author "Block" — the Apple strategy on a mac — so its store is ~/Library/Application Support/Block/goose, and deja looked in ~/.local/share/goose on every platform: a mac user who had used goose was told goose missing (0 files), which reads as never having used it. Every candidate root that exists is now read rather than the first, so an install predating the change keeps working, and deja doctor and deja sources name each directory they looked in. The machine this was written on has the old layout, which is why a single root looked right from inside it. (#3642)
  • A Codex rollout older than seven days is read again. Codex compresses one to rollout-*.jsonl.zst after that (COMPRESSED_SUFFIX, MIN_ROLLOUT_AGE in its own rollout/src/compression.rs, run by a background worker) and reads either name itself, while deja wanted .jsonl alone — so on a machine where that worker had run, every session older than a week left the index without a word: a file that is not a candidate is not a skip either. Reading one needs the zstd CLI, and a store that holds compressed rollouts without it now says zstd CLI not found the way the DeepSeek Harness and Zed stores already do. archived_sessions/ is read beside sessions/ — Codex's own second directory, same JSONL — and the same session under both names is read once, plain copy first, because Codex materializes a rollout back before appending to it. Index format 48. (#3640)
  • A value whose label says it is public stays readable. The entropy pass takes the word before the separator as the label, so a WireGuard dump — public key: <base64>, once per interface and once per peer — read as key: and every one of those lines was stored as [redacted:entropy]. On a 2,719-session store that was the largest single class inside the entropy tier, which is half of all redaction, and none of it is a credential: a published key masked in the index is a line no question about it can match. peer:, api key:, secret key =, private key: and a bare key: are unchanged, and api_key= was never the entropy pass's to begin with. Index format 47 — a store built before it keeps the masked text until it re-reads its sources. (#3638)
  • The repository-root lookup gets a budget windows can meet. git worktree list is 5 ms where processes are cheap and the 400 ms bound was sized for that; a cold git.exe misses it, and what a user loses is the project scoping for an agent started in a subdirectory — silently, since the lookup is best-effort. Two seconds on windows, unchanged elsewhere, and the test that pins the behaviour names a machine slower than the budget instead of failing on it. (#3624)
  • A release says what the repository still has to catch up on. Two steps are left behind by design — the Windows manifests are attached to the release rather than committed, and each extension's version is decided by npm and written into a temporary copy — and on 15 September both were forgotten after 0.20.1: the manifests turned every later pull request red, which is how anyone found out. The reminder now arrives with the release, as one issue that is commented rather than reopened, carrying the two commands. (#3627)
  • Each published extension's version in the repository is what npm serves. The release writes the number into a temporary copy, so the files here never learned it: extensions/opencode/package.json said 0.1.2 for a package npm serves as 0.20.1, and all four were behind. The guard that should have said so compared npm against the newest release tag — a rule that stopped being true when a release ahead of npm started publishing the next patch of that package's own line rather than skipping it (#2993), and one a shallow CI checkout could never evaluate, since it has no tags. It compares the repository against npm now, and node scripts/extension-drift.mjs --write is the catch-up. (#3627)
  • deja forget --session and --project exit non-zero when the selector names nothing. deja forget --session $ID && echo removed printed "removed" for a session still on disk under another id — a script reads the exit code, not the wording, and the sibling --unforget has refused a miss since #2263. A window is not a name: --before 30d on a young store still exits 0. (#3601)

Changelog

Features

  • feat(cli): one refusal for a mistyped flag, from eleven more commands (#1829) (#3728) (@vaibhav8a)
  • feat(doctor): report a stale Grok plugin, as it already does for Kimi (#1828) (#3733) (@vaibhav8a)
  • feat(index): --quiet for the runs nobody is watching (#1827) (#3727) (@vaibhav8a)
  • feat(install): wire Kiro, Kimchi, gajae-code and ZCode, and make gap entries say something (#3652)
  • feat: deja blame : names the session that wrote the line (#3722)
  • feat: read six more stores — Kiro, Senpi, Kimchi, gajae-code, Command Code, ZCode (#3649)
  • feat: senpi is wired, and doctor stops miscounting extension state (#3671)
  • feat: the first-screen suggestion comes from what was asked (#3724)

Fixes

  • fix(doctor): name the command that updates the Grok plugin (#3736)
  • fix(hooks): give the git budgets a size windows can meet (#3646)
  • fix(redact): a label that says public is not a secret (#3639)
  • fix(tests): make the index --quiet failure case fail on every platform (#3734) (@vaibhav8a)
  • fix: a repository-root lookup budget windows can meet (#3625)
  • fix: hook lines a shell can run, a record of real targets, and two configs deja could not read (#3694)
  • fix: install and uninstall answer for everything they write (#3693)
  • fix: mask a password stated in prose (#3730)
  • fix: the answers an agent reads, measured and corrected (#3719)
  • fix: the extension versions are what npm serves, and a release says what is left (#3628)
  • fix: the first-screen suggestion needs a phrase that recurs (#3715)
  • fix: the newest harnesses on their own clients — Kilo's CLI, Cherry Studio's validator (#3673)
  • fix: twelve things only the harness's own screen said (#3658)
  • fix: two stores that lost history, and two harnesses whose format we already read (#3641)
  • fix: uninstall takes back the directories it made, and a skill names its own directory (#3701)

Performance

Documentation

  • docs(registry): every dash gets its reason, including four that had none (#3653)
  • docs: Chinese guides for dsh, Kimi Code and Qwen Code (#3630)
  • docs: cut 0.20.2 (#3737)
  • docs: guide pages for Kiro, Kilo Code and Cherry Studio (#3650)
  • docs: per-agent titles in the words people search (#3634)
  • docs: the delete page answers the question in its first line (#3632)
  • docs: titles, dates and three pages for the search intent we already rank for (#3633)

Other

  • bench: commit the runs behind the published numbers (#3635)
  • chore: pin the manifests to 0.20.1 (#3626)
  • ci: decide the windows leg in the run that posts the check (#3708)

Install

curl -fsSL https://raw.githubusercontent.com/vshulcz/deja-vu/main/install.sh | sh
go install github.com/vshulcz/deja-vu/cmd/deja@v0.20.2
brew install deja-vu

Don't miss a new deja-vu release

NewReleases is sending notifications on new releases.