{"_id":"@bigtreeproduction/deja-vu","_rev":"3-f7dcaa256147c145e01d5078db05e046","name":"@bigtreeproduction/deja-vu","dist-tags":{"latest":"0.2.1"},"versions":{"0.1.0":{"name":"@bigtreeproduction/deja-vu","version":"0.1.0","keywords":["memory","cli","agent","mcp","claude"],"license":"UNLICENSED","_id":"@bigtreeproduction/deja-vu@0.1.0","maintainers":[{"name":"bigtreeproduction","email":"mike@bigtreeproduction.com"}],"bin":{"vu":"dist/cli.js"},"dist":{"shasum":"dad0a5c81316e5cec3a8adcc60145d802a80556c","tarball":"https://registry.npmjs.org/@bigtreeproduction/deja-vu/-/deja-vu-0.1.0.tgz","fileCount":63,"integrity":"sha512-aZDhbObuu2wqkwjwYAYLYIEMrkq/EKCQclIgxJ/a02HrR+leyeiYCYxr8xXI2BEyQdPY/SmZKl9Bk25Jgak6VQ==","signatures":[{"sig":"MEYCIQD9aJbs06rJtVwSUXbyz7rur5Wadjl2VfDYYzNBX8SRtgIhALo3sYgm9xUloEjfUKX1j5Cj2WF6zRkTCGoPwprbslDR","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":188064},"type":"module","engines":{"node":">=20"},"gitHead":"ec64277d78f3c8926937ade964caf34c8d85aa1b","scripts":{"dev":"tsx src/cli.ts","test":"vitest run","build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit","test:watch":"vitest","generate:types":"openapi-typescript ../deja_sh/apps/api/openapi.json -o src/api-types.d.ts","prepublishOnly":"npm run typecheck && npm run test && npm run build"},"_npmUser":{"name":"bigtreeproduction","email":"mike@bigtreeproduction.com"},"_npmVersion":"11.9.0","description":"Thin client for the deja.sh memory API","directories":{},"_nodeVersion":"24.14.0","dependencies":{"commander":"^12.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","vitest":"^2.1.0","typescript":"^5.5.0","@types/node":"^20.14.0","openapi-typescript":"^7.4.0"},"_npmOperationalInternal":{"tmp":"tmp/deja-vu_0.1.0_1788189246328_0.38631042954974526","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@bigtreeproduction/deja-vu","version":"0.2.0","keywords":["memory","cli","agent","mcp","claude"],"license":"UNLICENSED","_id":"@bigtreeproduction/deja-vu@0.2.0","maintainers":[{"name":"bigtreeproduction","email":"mike@bigtreeproduction.com"}],"bin":{"vu":"dist/cli.js"},"dist":{"shasum":"6eee309cdc32c90aaaed24f3fea8d8dd5a35b31a","tarball":"https://registry.npmjs.org/@bigtreeproduction/deja-vu/-/deja-vu-0.2.0.tgz","fileCount":70,"integrity":"sha512-w/NUg/IETSIndBOiCyJlgxeuIzMiLTf1LK7YpZ2MLUBskkLBDQSrwhqt9AGhzJby/9N7RvPY6YiEXptI1FYtIQ==","signatures":[{"sig":"MEYCIQDHo8YQG28JQbYo8YSygGd5x4ZfjlV07updvfyn6zhIngIhAIx15RS2iVsGHNZF4/ouxQ/Xwtrw7OYy5AhbVSYtAt4F","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":246938},"type":"module","engines":{"node":">=20"},"gitHead":"37d99ea9f407e2dc8200ed721f40175fb7e8a568","scripts":{"dev":"tsx src/cli.ts","test":"vitest run","build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit","test:watch":"vitest","generate:types":"openapi-typescript ../deja_sh/apps/api/openapi.json -o src/api-types.d.ts","prepublishOnly":"npm run typecheck && npm run test && npm run build"},"_npmUser":{"name":"bigtreeproduction","email":"mike@bigtreeproduction.com"},"_npmVersion":"11.9.0","description":"Thin client for the deja.sh memory API","directories":{},"_nodeVersion":"24.14.0","dependencies":{"commander":"^12.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","vitest":"^2.1.0","typescript":"^5.5.0","@types/node":"^20.14.0","openapi-typescript":"^7.4.0"},"_npmOperationalInternal":{"tmp":"tmp/deja-vu_0.2.0_1788545348691_0.28974929141037165","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"_id":"@bigtreeproduction/deja-vu@0.2.1","bin":{"vu":"dist/cli.js"},"dist":{"shasum":"315b2e3ab705db61c1bd23ad396cc03db97a5cd9","tarball":"https://registry.npmjs.org/@bigtreeproduction/deja-vu/-/deja-vu-0.2.1.tgz","fileCount":73,"integrity":"sha512-l1dBmNiSFoaiTc+N6S+QOFd8mXSDKzzWHIL65cesZ6l1xfIPE4f0cJSo5+rO5uPPkLjr4JuLsvUC8j8o+DuVBg==","signatures":[{"sig":"MEQCIBa31ID9OeyW9a6AT1bXmgYjWIWtff/TJRZVv4MSSpVVAiBPtnv+zgGSIyApKyr4ln6dqyGc/+rmGO3d/b8ict2pnw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCVM+XVTTNipXHTJU+Xi70OoedqSUD2X+HQlyxK8Vrx0gIhAJbT+hUXp8MLSCtShaLaDsySGMEtbQ5VBDH7cEixYIuq"}],"unpackedSize":273505},"name":"@bigtreeproduction/deja-vu","type":"module","author":{"name":"Mike","email":"mike@bigtreeproduction.com"},"engines":{"node":">=20"},"gitHead":"ad540a4ccc87d7ce6ead51583c46e8cf959bb32e","license":"LicenseRef-Proprietary","scripts":{"dev":"tsx src/cli.ts","test":"vitest run","build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm run test && npm run build"},"version":"0.2.1","_npmUser":{"name":"bigtreeproduction","email":"mike@bigtreeproduction.com"},"keywords":["memory","cli","agent","mcp","claude"],"_npmVersion":"11.9.0","description":"Thin client for the deja.sh memory API","directories":{},"maintainers":[{"name":"bigtreeproduction","email":"mike@bigtreeproduction.com"}],"_nodeVersion":"24.14.0","dependencies":{"commander":"^12.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","vitest":"^2.1.0","typescript":"^5.5.0","@types/node":"^20.14.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/deja-vu_0.2.1_1789931139515_0.055083926249818393"}}},"time":{"created":"2026-08-31T15:14:06.187Z","modified":"2026-09-20T19:05:39.745Z","0.1.0":"2026-08-31T15:14:06.506Z","0.2.0":"2026-09-04T18:09:08.835Z","0.2.1":"2026-09-20T19:05:39.595Z"},"license":"LicenseRef-Proprietary","keywords":["memory","cli","agent","mcp","claude"],"description":"Thin client for the deja.sh memory API","maintainers":[{"name":"bigtreeproduction","email":"mike@bigtreeproduction.com"}],"readme":"# vu\n\nThe `vu` CLI — a thin client for the deja.sh memory API.\n\n**Status:** published to npm as\n[`@bigtreeproduction/deja-vu`](https://www.npmjs.com/package/@bigtreeproduction/deja-vu) **v0.2.0**\n(2026-09-04). Every command below is implemented against a live server, not\nstubbed.\n\n> **Upgrading from 0.1.0?** One breaking change: a `403` now exits **8**\n> (refused) rather than **4** (auth). Re-read anything that branches on exit\n> codes — a script retrying on `4` would previously loop forever re-logging-in\n> on a refusal that re-authentication cannot clear. 0.2.0 also adds the whole\n> **Teams** section below, `--replace-trigger`, exit **7** for rate limits, and\n> a hook-path notice so a throttled recall is never mistaken for an empty vault.\n\nWhat is pending for this client and the server half together — the release,\nthe product decisions, the testing — is one list in the deja_sh repo:\n`docs/vu/todo.md`. Bugs are tracked separately, in `docs/vu/review-2026-09-04.md`.\n\nHow this client is (and is not) tied to the server's contract:\n[`docs/api-contract.md`](docs/api-contract.md). Short version — not at the type\nlevel, because the API declares response schemas for none of its `/vu/*`\noperations, so there is nothing to derive a response type from.\n\nThe package name is scoped because npm rejected the unscoped `deja-vu` as too\nsimilar to the long-dormant `dejavu` package — see\n[`docs/publishing-npm.md`](docs/publishing-npm.md) §5. The installed **command\nis still `vu`**; the scope is typed once, at install.\n\nDesign and build plan live in the **deja_sh** repo, `docs/vu/`:\n`vu-client-design.md` and `vu-client-implementation.md`. This repo is the only\ncopy of the client source.\n\n## Quickstart\n\n```bash\nnpm install -g @bigtreeproduction/deja-vu   # requires Node >= 20 (native fetch)\nvu login --token <your-pat>     # stored 0600 in ~/.vu/auth.json\nvu setup claude-code            # merges into your config; never overwrites\n```\n\n**`vu setup` installs hooks, not MCP config.** It registers the session-start\nand command-boundary recall hooks (Claude Code only — the others have no hook\nboundaries) and then tells you how to connect MCP. It deliberately does not\nwrite `mcpServers` entries: vu is hosted, and every agent reaches it through\na connector or its own CLI command —\n\n- **claude.ai web / mobile / Desktop** — add a custom connector for\n  `https://api.deja.sh/vu/mcp`, click Connect, paste a vu PAT once. One\n  connection covers all three.\n- **Claude Code** — `claude mcp add --transport http vu https://api.deja.sh/vu/mcp --header \"Authorization: Bearer <PAT>\"`\n- **Codex / Gemini CLI** — no hosted-connector support yet.\n\nEarlier versions wrote an `mcpServers.vu` entry for all four agents and not\none of them read it, so setup reported success while doing nothing.\n\nThen paste [`docs/claude-md-block.md`](docs/claude-md-block.md) into your\n`CLAUDE.md`. That block is the protocol an agent follows — without it the tool is\ninstalled but nothing knows when to use it.\n\n```bash\nvu load --context \"what I'm working on\"     # start of a session\nvu search \"topic\"                            # start of each new task\nvu save \"the thing you just learned\" -t gotcha\nvu applied <id> --scenario \"...\" --reason \"...\"\nvu applications update <id> --outcome contradicted   # correct a stamp\n```\n\n**Working on vu itself:**\n\n```bash\nnpm install\nnpm run build\nnpm test\nnode dist/cli.js --help\n```\n\n## Commands\n\n**Auth**\n\n```\nvu login --token <pat>       store a PAT in ~/.vu/auth.json (0600, in a 0700 dir)\nvu logout                    revoke server-side, THEN wipe the local file\nvu whoami                    the account this token belongs to\n```\n\n**Write**\n\n```\nvu save \"<content>\" -t <type>   one fact per memory (2000 char cap)\n    --project <name> | --global    scope; defaults to the git root\n    --category user|agent          user preferences decay 5x slower\n    --file <path> | --file -       read content from a file or stdin\n    --relate <ids>                 comma-separated; repeating the flag UNIONS\n    --trigger \"<cmd, cmd>\"         surface this gotcha before those commands\n                                   (trigger matching is literal substring, so\n                                   tag exact phrases — a bare common word pins\n                                   the memory on unrelated queries; fix one with\n                                   `vu update --replace-trigger`)\nvu update <id>                  metadata only — there is no --content, by design\n    --trigger --type --project --relate --unrelate\n    --replace-trigger \"<a, b>\"     REPLACES the trigger list; \"\" clears it\n```\n\n**Read**\n\n```\nvu search \"<query>\"          hybrid; -n is a DISPLAY cap, not a retrieval cap\n    --command \"<raw cmd>\"      raw text for the deterministic trigger match\nvu load --context \"<what>\"   the session-start bundle (project derived from\n                             the git root, like save; -p overrides)\nvu show <id...>              several ids in one call; prints trigger phrases and\n                             linked ids; --applications for history\nvu list                      --archived --by --sort --limit 0\nvu stats                     counts, token estimate, embedding coverage\nvu applications stats        validation health\n```\n\n**Lifecycle**\n\n```\nvu applied <id...>           record that a memory CHANGED WHAT YOU DID\n    --scenario --reason --ref --outcome --strength\nvu archive <id>              recoverable; content untouched\nvu unarchive <id>            floors confidence at 0.7 so decay cannot re-archive it\nvu invalidate <id>           it turned out WRONG; flips recent applications too\n```\n\n**Maintenance and data**\n\n```\nvu reflect                   corpus-only passes: decay, promote, dedup, archive\n    --dry-run                  same counts, writes nothing\n    --prompt [--after <id>]    the codebase-aware half, for an agent to run\n    --team [<name>]            act on a TEAM pool instead of your own\n    --apply                    commit a TEAM sweep (a team run previews first)\n    --runs | --undo <runId>    the run log, and putting a run back\nvu export --out <file>       everything as JSONL, verified complete before rename\n    --verify <file>            check a file you already have\nvu restore <file>            read an export back through the ordinary save path\nvu setup <agent>             claude-code | claude-desktop | codex | gemini\n    --dry-run --force\n```\n\n**Teams**\n\n```\nvu team                      the teams you are in, with your role (= team list)\nvu team members [<name>]     who is in one; --team <name> also works\nvu team create <name>        a new team, with you as its owner\nvu team add-member <email>   add someone, or change their role — OWNER ONLY\n    --team <name> --role owner|member\nvu team remove-member <email>  their memories stay — OWNER ONLY\n    --team <name>\nvu team delete <name>        previews unless --yes; destroys no memories\nvu share <id> [--team <n>]   move a memory into a team pool\nvu unshare <id>              move it back to your own — AUTHOR ONLY\n```\n\nGetting a team going takes two commands:\n\n```\nvu team create Platform\nvu team add-member colleague@example.com --team Platform\n```\n\nThey need a vu account already — a missing one is an error naming the address,\nrather than an invitation that silently does nothing.\n\n`init`, `embed`, `sync` and `viewer` are retired and print what replaced them.\n`_hook-field` and `_hook-emit` are internal, called by generated hook scripts.\n\n## Two pools: read wide, write narrow\n\nIf you are in a team you have two pools — your own memories, and the team's.\nThe rule is one sentence: **you read from both unless you narrow; you write to\nyour own unless you say `--team`.**\n\n```\nvu search \"q\"                both pools — the default, and no flag needed\nvu search \"q\" --team         the team pool only\nvu search \"q\" --personal     your own unshared memories only\nvu save \"...\" --team         save INTO the team pool\n```\n\n`--team` never changes what a command *does*, only which pool it acts on:\nthe destination on a write, the span on a read. Bare `--team` means \"my sole\nteam\" and fails with a usage error if you are in none or in several — pass a\nname (`--team Acme`) when you are in more than one.\n\nReads default to the union so **no agent configuration changes when you join a\nteam**: the generated hook scripts pass no pool flag, and a teammate's gotcha\nstarts appearing in recall by itself. `--team` with `--personal` is a usage\nerror rather than a precedence puzzle; picking a winner would make the other\nflag a silent no-op.\n\nA few asymmetries worth knowing:\n\n- **`reflect --team` previews.** A personal `vu reflect` commits; a team sweep\n  prints the same counts and writes nothing until you add `--apply`. It is\n  someone else's memories at stake, so the destructive form is opt-in.\n  `--prompt --team` needs no `--apply` — the dump writes nothing at all; it\n  hands the agent one pool's rows, and the commands it names carry `--team`\n  through so the sweep it describes stays in that pool.\n- **`unshare` is author-only.** It moves a row into *its author's* private\n  pool, which is a transfer of custody a non-author does not own. To retire a\n  team memory for everyone, `vu archive` it — that is what \"this should not be\n  here\" usually means. A non-author gets exit `8`.\n- **Everything else on a team row is open to every member.** Editing,\n  archiving, invalidating, applying, reflecting. Your `role` gates membership\n  changes — adding and removing people, renaming, deleting the team — not\n  knowledge. The pool grants the right to curate; the role grants the right to\n  change who is in the pool.\n- **Shared rows show their author**, and `former member` once that account is\n  gone. Your own personal rows are unlabelled — every row with an author is a\n  shared one.\n- **Creating a second team changes what bare `--team` means.** It resolves only\n  when you are in exactly one team, so from your second team onward it needs a\n  name — everywhere it is already used, including generated hooks. `vu team\n  create` warns you at the moment this happens, which is the only place it is\n  cheap to hear.\n- **The last owner cannot be removed.** An ownerless team can never change its\n  membership again, so promote someone else first\n  (`vu team add-member <email> --role owner`). An admin can repair it from the\n  panel if it happens.\n- **`vu team delete` previews unless you pass `--yes`**, and the preview is\n  real: it names who loses access and counts the shared memories. Those\n  memories are **not** destroyed — they return to their authors' personal pools\n  — but re-creating the team will not re-share them, so each would have to be\n  shared again individually.\n\n## Environment\n\n| variable | meaning |\n|---|---|\n| `VU_ENDPOINT` | API base URL; overrides the persisted one |\n| `VU_TOKEN` | PAT, for a sandbox that cannot write `~/.vu` |\n| `VU_HOME` | where `auth.json` lives (default `~/.vu`) |\n\n## Exit codes\n\n`0` ok · `2` usage · `3` not found · `4` auth · `5` offline · `6` server ·\n`7` rate limited · `8` refused.\n\nOffline has its own code because it is not a failed request but an absent one —\nvu has no offline queue, so the operation did not happen. A **timeout** exits\n`5` too (30s per request; 10min for the export stream), but with a message\nowning the difference: a request that timed out after connecting *may* have\nbeen processed — a re-run save at worst dedups into the copy that landed. Rate limited has its\nown for the same kind of reason: it is neither your mistake nor a broken server,\nit is \"not now\", and a script needs to branch on that without parsing text.\n\n`8` refused is every 403: the request was understood, you are known, and the\nanswer is no. It is separate from `4` auth because **logging in again cannot\nhelp** — a suspended account stays suspended, and `unshare` on someone else's\nmemory stays theirs. A script retrying on `auth` would loop. (This moved: 403\nused to report `4`.)\n\nOn the **hook path** a 429 does not exit non-zero at all. The generated hook\nscripts discard stderr and treat empty stdout as \"inject nothing\", so a\nthrottled recall was indistinguishable from a vault with nothing to say — the\nagent proceeded with no recall and nothing said so. `--format hook` writes one\nline to stdout instead, saying recall was skipped and the vault was not\nconsulted. Only 429, and only that format: a 401 is not transient, so\nsoftening it would hide a real fault.\n\nThis holds for **both** hook renderers — `search` (per-prompt recall) and\n`load` (session start). Until 2026-09-04 only `search` had the guard, and this\nsection described the mitigation as if it covered the hook path while a\nrate-limited session start silently began from an \"empty\" vault — the larger\nloss of the two, since the agent then re-derives and re-saves what it already\nhad. The guard is one shared implementation now, and a source-surface test\nfails the build if a future hook renderer skips it — the property is not\nre-decided per command.\n\n---\n\n## The three design rules\n\nThese are the rules a code review can check mechanically. They are not\naspirations.\n\n### 1. No decisions in the client\n\nIf a command needs to **judge** something about a memory — what ranks higher,\nwhat is a duplicate, what should decay — that belongs on the server. The client\nformats input and output.\n\nThe test: if a change to the answer would change what a user sees *and* is not a\nformatting choice, it is a decision. Sorting search results is a decision (the\nserver already ranked them). Truncating a body to 100 characters for a headline\nis formatting.\n\nThis holds even where the client reads a local file. `save-session` and the\nlocal half of `reflect` work by the server supplying a prompt, the **agent**\njudging with its own model and its own filesystem access, and the result coming\nback as ordinary API calls. `vu` relays and applies; it never decides.\n\n### 2. Dependencies stay near zero\n\nNative `fetch`, one CLI parser. A dependency that implies storage, embedding, or\nranking means logic has leaked back into the client. This is a tripwire, not\nasceticism: `better-sqlite3` or `onnxruntime` appearing in `package.json` is a\ndesign regression a reviewer can catch without reading any code.\n\n### 3. The server owns error text\n\nIt owns the semantics, so it owns the message worth showing. The client adds only\nwhat the server cannot know: whether stdout is a TTY, whether the user is logged\nin, and what to suggest when the network is gone.\n\n---\n\n## Boundaries\n\n**All API paths are under `/vu/v1/`.** The same backend serves another product\non `/v1/*`; `vu` never calls those.\n\n**All filesystem access is confined to `src/local/`, and all of it stays inside\n`~/.vu/`.** Nothing here reads or writes another tool's config directory — doing\nso would let a `vu login` against a dev server silently repoint something else.\nBoth halves of that are lint-able and should be linted.\n\n**v0 scope.** No offline queue (a save that cannot reach the API fails, loudly),\nno local read cache, no file watcher, no bulk backfill or import. A new account\nstarts empty and fills as the agent works.\n\n## Publishing\n\nDone from the machine with the npm account — full runbooks in\n[`docs/publishing-npm.md`](docs/publishing-npm.md) (npm) and\n[`docs/publishing-homebrew.md`](docs/publishing-homebrew.md) (brew tap).\nnpm first; the brew formula wraps the published npm tarball.\n\n","readmeFilename":"README.md","author":{"name":"Mike","email":"mike@bigtreeproduction.com"}}