diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0bd5a9b..8726a18 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,9 @@ on: - main pull_request: +permissions: + contents: read + jobs: test: runs-on: ubuntu-latest @@ -26,6 +29,9 @@ jobs: - name: Source tests run: npm run test:src:readonly + - name: Markdown links + run: npm run check:links + - name: Memory lint run: npm run lint:memory diff --git a/CHANGELOG.md b/CHANGELOG.md index aa3c880..5e98c3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ All notable changes to this project will be documented in this file. +## [0.7.0] - 2026-08-09 + +- Rebuilt the README around a one-command start, concrete before/after proof, agent compatibility, safety boundaries, and a shareable product visual. +- Added `deja-vu init --merge-agents` to append an idempotent rules block without replacing an existing `AGENTS.md`. +- Made `init` report incomplete setup and made `doctor` reject an `AGENTS.md` that lacks Deja Vu recall rules. +- Added cross-platform starter-kit instructions and a contributor guide. +- Added repository-wide local Markdown link validation and fixed broken memory-index template links. +- Hardened package verification to use an isolated npm cache and prepared the package metadata for the first CLI-capable npm release. + ## [0.6.0] - 2026-05-17 - Reworked the README first screen around the "3-file memory system" positioning. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..c1d3822 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,42 @@ +# Contributing to Deja Vu + +Thanks for helping make project memory simpler and safer for coding agents. + +## Product boundary + +Deja Vu is protocol-first: + +- the three-file starter kit is the main product +- Markdown and JSONL remain canonical and human-readable +- the CLI is an optional helper +- the TypeScript engine is an optional scale-up layer + +Changes should reduce repeated explanation, recall cost, or memory noise without turning the base product into a hosted service or heavy framework. + +## Before opening a pull request + +1. Keep the change focused and explain the user problem it solves. +2. Add or update tests for behavior changes. +3. Keep public examples sanitized; never add secrets, PII, raw transcripts, or private project facts. +4. Run the relevant verification: + +```bash +npm ci +npm run test:src:readonly +npm run check:links +npm run lint:memory +npm run report:feedback +npm test +``` + +`npm test` rebuilds `dist` and runs npm package verification. `npm run test:src:readonly` is the read-only source-test path. + +## Good first contributions + +- clearer starter-kit instructions +- compatibility notes backed by actual agent behavior +- safer memory lint rules with low false-positive rates +- small, sanitized protocol examples +- documentation fixes and reproducible bug reports + +For larger protocol or architecture changes, open an issue first so the behavior and compatibility impact can be discussed before implementation. diff --git a/README.md b/README.md index 45e9e47..2afc2dc 100644 --- a/README.md +++ b/README.md @@ -1,124 +1,111 @@ -# Deja Vu +# Deja Vu — project memory for coding agents -**A 3-file memory system for any capable AI coding agent.** +[![CI](https://github.com/focaxisdev/deja-vu/actions/workflows/ci.yml/badge.svg)](https://github.com/focaxisdev/deja-vu/actions/workflows/ci.yml) +[![npm](https://img.shields.io/npm/v/@focaxisdev/deja-vu?logo=npm)](https://www.npmjs.com/package/@focaxisdev/deja-vu) +[![License: MIT](https://img.shields.io/badge/license-MIT-70f0c0.svg)](./LICENSE) +[![Node.js 20+](https://img.shields.io/badge/node-%3E%3D20-7aa7ff.svg)](./package.json) -Stop re-explaining your repo every time you start a new Codex, Claude Code, Cursor, Windsurf, ChatGPT, or Gemini CLI session. +**Stop re-explaining your repo every time you open a new AI coding-agent session.** -Deja Vu gives your project a tiny, repo-local memory layer: - -- `AGENTS.md` tells agents how to recall and write memory. -- `memory/summary.md` keeps durable project context. -- `memory/impressions.jsonl` stores tiny recall cues. +Deja Vu is a three-file, repo-local memory system that any capable coding agent can follow. It keeps durable decisions and constraints beside the code, then loads only the smallest useful slice for the current task. No database. No vector store. No embeddings. No SaaS. No daemon. No required npm install. -Just copy three files into your repo. +![Deja Vu turns tiny repo-local cues into minimal, relevant recall](./docs/assets/deja-vu-preview.svg) -```text -Task - -> scan tiny cues - -> no familiarity: load nothing - -> weak familiarity: load summary - -> strong familiarity: load 1-3 linked records - -> work - -> write back durable memory only -``` +## Start in 60 seconds -Deja Vu is not trying to store more. -Deja Vu is trying to recall less, better. +In a fresh repo, run: -## 2-Minute Start +```bash +npx @focaxisdev/deja-vu init +``` -Option A: copy from this repo after cloning: +That creates only the three required files: -```bash -cp -R starter-kit/. . +```text +AGENTS.md +memory/summary.md +memory/impressions.jsonl ``` -Option B: use the optional CLI: +Already have an `AGENTS.md`? Preserve it and append an idempotent, clearly marked Deja Vu rules block: ```bash -npx @focaxisdev/deja-vu init --agents all +npx @focaxisdev/deja-vu init --merge-agents ``` -Then paste this into your next agent session: +Inspect the plan without touching files, or verify the finished setup: -```text -Follow AGENTS.md. Before substantial planning or code changes, scan memory/impressions.jsonl for familiar cues. If familiarity is weak, read memory/summary.md. If familiarity is strong, read only the 1-3 linked detailed records needed for this task. Do not load the whole memory tree unless I ask. After the task, write back only durable decisions, architecture intent, stable preferences, unresolved follow-ups, or milestone summaries. Never store secrets, full transcripts, low-signal chatter, or disposable exploration noise. +```bash +npx @focaxisdev/deja-vu init --dry-run +npx @focaxisdev/deja-vu doctor ``` -That is the base product. Scripts, feedback, decision records, open loops, and the TypeScript engine are optional scale-up paths. +Then start the next agent session with: -## Before / After +> Follow AGENTS.md. Scan memory/impressions.jsonl before substantial work, load only the matching memory, and write back durable project context only. -Before Deja Vu: +Prefer a zero-package setup? Copy the [`starter-kit/`](./starter-kit) instead. npm is a convenience layer, not the product. -- "Remember, we already decided not to use X because Y..." -- "Here is the architecture again..." -- "Please do not break the IME workaround we fixed last week..." -- "The agent forgot the open issue from the previous session..." +## What changes -After Deja Vu: - -- "Follow AGENTS.md and scan memory/impressions.jsonl before planning." -- The agent loads only relevant project memory. -- Durable decisions survive across new chats. -- Low-value chat noise is not stored. +| Without Deja Vu | With Deja Vu | +| --- | --- | +| “Here is the architecture again…” | The repo carries a compact project summary. | +| “We already rejected that approach because…” | Accepted decisions survive new chats. | +| “Do not break the workaround from last week…” | Strong task cues route to one to three relevant records. | +| Load a huge notes file or old transcript | Scan tiny cues first; load nothing when there is no match. | +| Memory stays trapped in one vendor | Markdown and JSONL travel with the repo. | -## What It Is +Deja Vu does not replay old chats. It helps a new session recognize what it needs to know before planning. -Deja Vu is a repo-level memory convention: +## Why three files? -| It is | It is not | -| --- | --- | -| three repo-local files first | a vector database | -| Markdown and JSONL | a hosted memory service | -| agent-readable project rules | an agent runtime | -| git-friendly durable memory | a required npm package | -| low-token recall discipline | a full chat transcript archive | +### `AGENTS.md` — behavior -The memory lives in the repo, not inside one vendor. +Tells an agent when to recall, how much to load, what to write back, and what never belongs in memory. -Any capable coding agent can follow the protocol when it can read project files, follow instructions, respect a memory budget, and update files. For chat-only agents, paste the prompt and provide the relevant memory files manually. +### `memory/summary.md` — durable context -## The Three Files +Keeps the current objective, stable constraints, active priorities, accepted decisions, and unresolved follow-ups. It is compact project truth, not a log. -### `AGENTS.md` +### `memory/impressions.jsonl` — routing cues -Tells agents how to use Deja Vu: +Keeps tiny, inspectable routes from task language to the right memory file: -- read project instructions first -- scan cues before substantial work -- load summary only when needed -- load 1-3 detailed records for strong matches -- write back only durable memory -- never store sensitive or noisy content - -### `memory/summary.md` +```json +{"schema_version":1,"id":"settings-sync","scope":"project:your-repo","title":"Settings stay server-backed","keywords":["settings","sync","preferences","localstorage"],"record_path":"memory/decisions/settings-sync.md","updated":"2026-08-09","weight":0.9,"status":"active"} +``` -Keeps compact project truth: +This separation matters: rules tell the agent what to do, the summary restores broad context, and impressions prevent broad context from being loaded for every task. -- current objective -- durable constraints -- active priorities -- linked decisions -- unresolved follow-ups +## Recall flow -It is not a chat log. +```text +task + -> scan memory/impressions.jsonl + -> none: load nothing + -> weak: load memory/summary.md + -> strong: load 1-3 linked records + -> work + -> write back durable memory only +``` -### `memory/impressions.jsonl` +Default recall budget: -Keeps tiny cue routes: +- impression scan: always allowed +- summary: at most one file +- detailed records: one to three files +- full memory tree: only when the user explicitly asks -```json -{"schema_version":1,"id":"summary","scope":"project:your-repo","title":"Project summary","keywords":["architecture","constraints","priorities"],"record_path":"memory/summary.md","updated":"2026-05-16","weight":0.7,"status":"active"} -``` +The goal is not to store more. The goal is to recall less, better. -Agents scan these cues before spending tokens on bigger memory files. +## Agent compatibility -## Agent Prompts +The protocol is vendor-neutral. It is a strong fit when an agent can read repo files, follow project instructions, and update files. -Copy a short prompt for the tool you use: +Short prompts are included for: - [Codex](./starter-kit/prompts/codex.md) - [Claude Code](./starter-kit/prompts/claude-code.md) @@ -127,32 +114,36 @@ Copy a short prompt for the tool you use: - [ChatGPT](./starter-kit/prompts/chatgpt.md) - [Gemini CLI](./starter-kit/prompts/gemini-cli.md) -The prompts all enforce the same budget: +For chat-only tools, paste the rules and only the relevant memory files manually. Deja Vu is a protocol agents can follow; it does not claim every agent supports it automatically. See [agent compatibility](./docs/agent-compatibility.md). -- scan `memory/impressions.jsonl` first -- read `memory/summary.md` only for weak familiarity -- read at most 1-3 detailed records for strong familiarity -- never load the whole memory tree by default -- write back only durable memory +## Safe by design -## Optional CLI +Tracked memory is part of the repo and may be public. Write back only context that should change future work: -The CLI exists to support the three-file protocol. It is not required. +- accepted decisions and architecture intent +- stable project constraints and preferences +- unresolved follow-ups +- milestone summaries +- sparse recall-quality feedback + +Never store secrets, credentials, private keys, customer or user PII, full transcripts, raw logs, or disposable exploration noise. + +`deja-vu doctor` validates the required setup and flags obvious secrets, transcript-like content, oversized memory, malformed JSONL, and unresolved feedback routes. It is a guardrail, not a complete secret or PII scanner. + +## CLI reference + +The optional CLI serves the file-first protocol: ```bash -npx @focaxisdev/deja-vu init --dry-run npx @focaxisdev/deja-vu init --agents codex,claude-code +npx @focaxisdev/deja-vu init --merge-agents npx @focaxisdev/deja-vu doctor --json npx @focaxisdev/deja-vu explain ``` -`init` creates missing files only. It does not overwrite existing files unless you pass `--force`. - -`doctor` checks whether a repo has a usable Deja Vu setup, validates JSONL, warns about memory bloat and transcript-like content, and flags obvious secrets. +`init` creates missing files only. It never replaces existing files unless `--force` is explicitly passed. When `AGENTS.md` already exists without Deja Vu rules, the command reports that setup is incomplete; `--merge-agents` appends a marked block without deleting existing rules. -For public repos, remember that tracked memory files are public project files. `doctor` can catch obvious risks, but it is not a complete secret or PII scanner. - -Existing focused tools still work: +Focused tools are also available: ```bash deja-vu-scan-memory "current task" @@ -160,103 +151,64 @@ deja-vu-lint-memory --memory-root memory deja-vu-feedback-report --memory-root memory ``` -## Protocol - -The Deja Vu lifecycle is: +## Protocol and docs -1. Scan tiny cues. -2. Classify familiarity: none, weak, or strong. -3. Load the smallest useful memory. -4. Work normally. -5. Write back durable memory only. -6. Compact or retire stale memory when recall gets noisy. - -Default recall budget: +Deja Vu Protocol v0.4 defines the lifecycle: -- impression scan: always allowed -- summary: at most one file -- detailed records: one to three files -- full memory tree: forbidden unless explicitly requested +1. scan tiny cues +2. classify familiarity as none, weak, or strong +3. load the smallest useful memory +4. work normally +5. write back durable context only +6. compact or retire stale memory when recall gets noisy -Write back only: - -- accepted decisions -- architecture intent -- stable preferences -- unresolved follow-ups -- milestone summaries -- recall feedback that should tune future cues - -Never write back: - -- secrets, tokens, credentials, or private keys -- full turn-by-turn transcripts -- customer/user PII -- low-signal chatter -- disposable exploration noise - -Read the full spec in [docs/protocol.md](./docs/protocol.md). - -## Docs +Start here: - [Starter kit](./starter-kit/README.md) -- [Comparison](./docs/comparison.md) -- [Agent compatibility](./docs/agent-compatibility.md) -- [Flow diagram](./docs/diagrams/deja-vu-flow.md) - [Demo walkthrough](./docs/demo-walkthrough.md) - [Protocol](./docs/protocol.md) +- [Comparison with other memory approaches](./docs/comparison.md) - [Workflow](./docs/workflow.md) - [Markdown storage contract](./docs/storage-markdown.md) -- [Impression layer](./docs/impression-layer.md) - [Scripted recall](./docs/scripted-recall.md) -- [Launch copy](./docs/launch-copy.md) +- [Architecture](./docs/architecture.md) - [llms.txt](./llms.txt) -## Optional Engine Layer +## Optional semantic engine -This repository still includes the existing TypeScript semantic recall engine. - -Use it when you want: - -- stronger familiarity scoring -- threshold-gated summary and chunk loading -- embedding and vector ranking -- an engine-backed implementation of the Deja Vu protocol - -Do not treat the engine as the product center. It is an optional acceleration layer after the repo-local memory protocol is already useful. +The package also contains the original TypeScript semantic recall engine for projects that outgrow manual cue routing. It adds threshold-gated recall and plugin seams for embeddings, storage, scoring, and vector search while keeping Markdown memory canonical. ```bash npm install @focaxisdev/deja-vu ``` ```ts -const engine = new SemanticRecallEngine(config); - -await engine.addMemory(input); -await engine.scanImpressions(query); -await engine.recall(query); -await engine.getSummary(id); -await engine.getChunks(id); -await engine.updateMemory(id, input); -await engine.deleteMemory(id); +import { createInMemorySemanticRecallEngine } from "@focaxisdev/deja-vu"; + +const engine = createInMemorySemanticRecallEngine({ + thresholds: { strong: 0.6, weak: 0.45 }, +}); ``` -## Examples +Read the [engine guide](./docs/engine/semantic-engine.md) or run the [basic example](./examples/basic/index.ts). The engine is an optional acceleration layer, not the default adoption path. -- Protocol-first example: [examples/protocol-project](./examples/protocol-project) -- Engine example: `npm run example:basic` -- Engine example: `npm run example:agent-pm` -- Engine example: `npm run example:chat-memory` -- Engine example: `npm run example:task-assistant` +## Contributing + +Issues and focused pull requests are welcome. Read [CONTRIBUTING.md](./CONTRIBUTING.md) for the project boundaries and verification commands. ## Development ```bash -npm install -npm run build +npm ci npm run test:src:readonly +npm run check:links npm run lint:memory npm run report:feedback +npm test ``` -`npm run test` rebuilds `dist`. Use `test:src:readonly` when you want source tests without build output side effects. +`npm test` rebuilds `dist` and verifies package contents. Use `test:src:readonly` when you want source tests without build-output side effects. + +--- + +If Deja Vu saves you one repo re-explanation, [star the repository](https://github.com/focaxisdev/deja-vu) so other coding-agent users can find it. diff --git a/docs/assets/deja-vu-preview.svg b/docs/assets/deja-vu-preview.svg new file mode 100644 index 0000000..f9e7232 --- /dev/null +++ b/docs/assets/deja-vu-preview.svg @@ -0,0 +1,77 @@ + + Deja Vu: three-file project memory for AI coding agents + A coding task triggers a tiny cue scan, loads two matching memory records, and gives a new agent the right constraints before planning. + + + + + + + + + + + + + + + + + + + + + + DEJA VU · MEMORY + + Your repo remembers + the why. + Three files give every new coding-agent session + the smallest useful slice of project memory. + + + + 01 AGENTS.md + recall + writeback rules + + + 02 memory/summary.md + durable project truth + + + 03 memory/impressions.jsonl + tiny recall cues + + + + + + + + + + new agent session + + > Continue the settings refactor. + scan + memory/impressions.jsonl + + + strong match + settings · sync · migration + 2 records + + load + decisions/settings-sync.md + load + open-loops/settings-migration.md + + + ✓ knows before planning + Keep settings server-backed + Preserve the IME workaround + Continue the legacy-name migration + + + No database · No embeddings · No SaaS + diff --git a/docs/launch-copy.md b/docs/launch-copy.md index 60e4c79..1c1fa9c 100644 --- a/docs/launch-copy.md +++ b/docs/launch-copy.md @@ -1,51 +1,73 @@ # Launch Copy -## X / Threads +Use this copy only after the public npm quick start has been verified. Keep the promise precise: capable file-aware agents can follow Deja Vu; support is not automatic in every tool. -I got tired of re-explaining the same repo to every new AI coding agent session. +Project URL: -Deja Vu is a 3-file, repo-local memory layer any capable coding agent can follow. +## Short post -No vector DB. -No SaaS. -No embeddings. -No daemon. +I got tired of re-explaining the same repo to every new AI coding-agent session. -Memory should feel like recognition, not replay. +Deja Vu gives the repo its own memory using three files: + +- `AGENTS.md` +- `memory/summary.md` +- `memory/impressions.jsonl` + +It scans tiny cues first, loads only matching context, and writes back durable decisions—not entire chats. + +No database, embeddings, SaaS, daemon, or required install. + +`npx @focaxisdev/deja-vu init` + + ## LinkedIn -Every new AI coding agent session starts with the same tax: explain the repo, explain the decisions, explain the constraints, explain what not to break. +Every new coding-agent session starts with a context tax: explain the architecture, repeat old decisions, restate constraints, and warn the agent about workarounds it must not break. -Deja Vu is a 3-file memory system for AI coding agents: +I built Deja Vu around a smaller idea than most memory systems: the repository should carry its own durable memory. -- `AGENTS.md` -- `memory/summary.md` -- `memory/impressions.jsonl` +The base system is only three human-readable files. A tiny cue index decides whether the agent should load nothing, one compact summary, or one to three relevant records. The project stays portable across capable file-aware agents, and the memory stays reviewable in Git. + +No vector database. No hosted memory service. No transcript archive. + +Quick start: `npx @focaxisdev/deja-vu init` + + + +## Show HN + +Title: + +> Show HN: Deja Vu – three-file project memory for AI coding agents -It is repo-local, human-readable, git-friendly, and does not require a vector database, SaaS memory platform, embeddings, daemon, or npm install. +First comment: -The goal is not to store more. The goal is to help agents recall less, better. +> I built Deja Vu because coding agents kept losing the reasoning that lives between commits: rejected approaches, architecture intent, fragile workarounds, and unresolved follow-ups. +> +> The minimum setup is `AGENTS.md`, `memory/summary.md`, and `memory/impressions.jsonl`. The impressions file is a tiny cue router: no match loads nothing, a weak match loads one summary, and a strong match loads one to three linked records. The agent writes back only durable context after the task. +> +> It is intentionally a file convention first. The CLI only creates and checks those files; npm, embeddings, and the optional TypeScript engine are not required. I would especially value feedback on the three-file boundary, the recall budget, and whether the existing-AGENTS merge behavior feels safe. -## GitHub Release Intro +## GitHub release intro -Deja Vu v0.6.0 reframes the project around the adoption path that matters most: a 3-file, repo-local memory system for any capable AI coding agent. +Deja Vu v0.7.0 makes the public quick start real: the npm package now includes the unified CLI and starter kit, while the protocol remains file-first and package-optional. -This release adds a copy-first starter kit, per-agent prompts, clearer compatibility and comparison docs, a visual recall flow, launch copy, and an optional `deja-vu init/doctor/explain` CLI that serves the file-first protocol without making npm required. +This release adds safe merging for existing `AGENTS.md` files, stricter setup diagnostics, cross-platform onboarding, link validation, isolated package verification, and a clearer before/after product story. -## Hacker News Titles +## Alternate titles -- Show HN: Deja Vu, a 3-file memory system for AI coding agents +- Show HN: Stop re-explaining your repo to every new coding-agent session - Show HN: Repo-local memory for Codex, Claude Code, Cursor, and Windsurf -- Show HN: Stop re-explaining your repo to every new coding agent chat -- Show HN: Deja Vu stores less so coding agents recall better +- Deja Vu: project memory that fits in a pull request +- Store less, recall better: a file-first memory protocol for coding agents ## Taglines -- A 3-file memory system for any AI coding agent. -- Repo-local memory for coding agents. +- Three files. Durable project memory. Any capable coding agent. +- Your repo should remember the why. - Long-term project memory that fits in a pull request. -- Stop re-explaining your repo to every new agent chat. +- Stop re-explaining your repo to every new agent session. - Memory should feel like recognition, not replay. -- Store less, recall better. -- Your repo should carry its own agent memory. +- Store less. Recall better. diff --git a/docs/release-v0.7.0.md b/docs/release-v0.7.0.md new file mode 100644 index 0000000..a084bfa --- /dev/null +++ b/docs/release-v0.7.0.md @@ -0,0 +1,57 @@ +# Deja Vu v0.7.0 Release Candidate + +v0.7.0 is the first release candidate whose public npm package includes the three-file starter kit and unified CLI. + +## Why this release matters + +The repository reached v0.6.0, but npm `latest` remained at v0.1.0. That published package does not expose the `deja-vu` CLI, so the README's `npx @focaxisdev/deja-vu init` path cannot work until a new package is published. + +v0.7.0 closes that adoption gap and hardens the first-run experience for repos with existing project rules. + +## Included + +- one-command starter setup +- safe `AGENTS.md` merge mode +- setup-readiness diagnostics +- cross-platform copy instructions +- README product proof and social-preview asset +- local Markdown link validation +- isolated-cache npm pack verification + +The protocol remains Deja Vu Protocol v0.4 because the normative recall budget and writeback lifecycle have not changed. + +## Release verification + +Run from a clean checkout: + +```bash +npm ci +npm run test:src:readonly +npm run check:links +npm run lint:memory +npm run report:feedback +npm run build +npm run test:pack +npm pack --dry-run +``` + +Verify the packed file list contains: + +- `scripts/deja-vu.mjs` +- `starter-kit/AGENTS.md` +- `starter-kit/memory/summary.md` +- `starter-kit/memory/impressions.jsonl` +- all declared package binaries + +## Publish sequence + +1. Merge the reviewed v0.7.0 changes. +2. Confirm npm authentication with `npm whoami`. +3. Publish with `npm publish --access public`. +4. Verify `npm view @focaxisdev/deja-vu version bin --json` reports v0.7.0 and all four binaries. +5. Test `npx @focaxisdev/deja-vu init --dry-run` from an empty temporary directory. +6. Create the v0.7.0 GitHub release from the verified commit. +7. Update the GitHub description, topics, and social preview. +8. Use the prepared launch copy only after the public quick start succeeds. + +Do not publish, tag, or announce from an unverified working tree. diff --git a/docs/templates/memory/index.md b/docs/templates/memory/index.md index 6583c40..8971440 100644 --- a/docs/templates/memory/index.md +++ b/docs/templates/memory/index.md @@ -1,21 +1,21 @@ # Memory Index - Scope: `project:example-project` -- Active summary: [summary.md](../summary.md) -- Impression index: [impressions.jsonl](../impressions.jsonl) -- Event ledger: [events/](../events/) +- Active summary: [summary.md](./summary.md) +- Impression index: [impressions.jsonl](./impressions.jsonl) +- Event ledger: [events/](./events/) ## Active decisions -- [Decision template](../decisions/decision-template.md) +- [Decision template](./decisions/decision-template.md) ## Active open loops -- [Open loop template](../open-loops/open-loop-template.md) +- [Open loop template](./open-loops/open-loop-template.md) ## Context -- [Project context](../context/project-context.md) +- [Project context](./context/project-context.md) ## Superseded or archived diff --git a/llms.txt b/llms.txt index 63e6717..f1b2605 100644 --- a/llms.txt +++ b/llms.txt @@ -11,6 +11,7 @@ Deja Vu helps agents stop re-explaining the same repository context across new c - recall budget and outcome feedback that show whether memory helped, missed, or overloaded the task - writeback routing and feedback reports that keep remembered project context durable and maintainable - optional `deja-vu init`, `deja-vu doctor`, and `deja-vu explain` CLI support +- safe installation into repos that already have `AGENTS.md` through `deja-vu init --merge-agents` The minimum adoption path is three files: `AGENTS.md`, `memory/summary.md`, and `memory/impressions.jsonl`. It does not require npm, embeddings, vector search, a daemon, or a dedicated memory service. @@ -18,6 +19,7 @@ The minimum adoption path is three files: `AGENTS.md`, `memory/summary.md`, and - [README](./README.md): product overview and primary adoption path - [Starter Kit](./starter-kit/README.md): copy-first three-file setup and per-agent prompts +- [Contributing](./CONTRIBUTING.md): project boundaries and verification commands - [Comparison](./docs/comparison.md): how Deja Vu differs from vector DBs, hosted memory, runtimes, notes, and transcripts - [Agent Compatibility](./docs/agent-compatibility.md): which agents can follow the protocol and how chat-only agents can use it manually - [Flow Diagram](./docs/diagrams/deja-vu-flow.md): cue scan, minimal recall, durable writeback @@ -27,7 +29,7 @@ The minimum adoption path is three files: `AGENTS.md`, `memory/summary.md`, and - [Markdown Storage](./docs/storage-markdown.md): minimum and optional memory layout - [Impression Layer](./docs/impression-layer.md): compact keyword-first familiarity surface - [Scripted Recall](./docs/scripted-recall.md): host script contract for cheap pre-task scans -- [v0.6.0 Candidate](./docs/release-v0.6.0.md): adoption release checklist +- [v0.7.0 Candidate](./docs/release-v0.7.0.md): npm, GitHub, and launch readiness checklist - [Templates](./docs/templates): copyable rules and memory templates - [Protocol Example](./examples/protocol-project): repo-first example with no engine diff --git a/memory/impressions.jsonl b/memory/impressions.jsonl index 2f53f39..1932d27 100644 --- a/memory/impressions.jsonl +++ b/memory/impressions.jsonl @@ -1,6 +1,7 @@ {"schema_version":1,"id":"positioning-three-file-memory","scope":"project:deja-vu","title":"Three-file memory positioning","keywords":["positioning","three-file","memory","coding-agent","repo-local"],"aliases":["3-file","product-positioning"],"record_path":"memory/summary.md","updated":"2026-05-17","weight":0.9,"status":"active"} {"schema_version":1,"id":"starter-kit-main-product","scope":"project:deja-vu","title":"Starter kit is the main product","keywords":["starter-kit","copy-first","AGENTS","summary","impressions"],"aliases":["three-file-starter"],"record_path":"memory/summary.md","updated":"2026-05-17","weight":0.85,"status":"active"} {"schema_version":1,"id":"protocol-first-not-engine-first","scope":"project:deja-vu","title":"Protocol-first, not engine-first","keywords":["protocol","engine","optional","framework","typescript"],"aliases":["engine-first","protocol-first"],"record_path":"memory/summary.md","updated":"2026-05-17","weight":0.85,"status":"active"} -{"schema_version":1,"id":"cli-helper-scope","scope":"project:deja-vu","title":"CLI is an optional helper","keywords":["cli","init","doctor","explain","optional","helper"],"aliases":["deja-vu-cli"],"record_path":"memory/summary.md","updated":"2026-05-17","weight":0.8,"status":"active"} +{"schema_version":1,"id":"cli-helper-scope","scope":"project:deja-vu","title":"CLI is an optional helper","keywords":["cli","init","doctor","explain","merge-agents","optional","helper"],"aliases":["deja-vu-cli"],"record_path":"memory/summary.md","updated":"2026-08-09","weight":0.8,"status":"active"} {"schema_version":1,"id":"safety-privacy-boundary","scope":"project:deja-vu","title":"Safety privacy boundaries","keywords":["safety","privacy","secrets","PII","transcripts","noise"],"aliases":["memory-safety"],"record_path":"memory/summary.md","updated":"2026-05-17","weight":0.85,"status":"active"} -{"schema_version":1,"id":"v060-release-readiness","scope":"project:deja-vu","title":"v0.6.0 release readiness","keywords":["v0.6.0","release","readiness","checklist","candidate"],"aliases":["release-candidate"],"record_path":"memory/summary.md","updated":"2026-05-17","weight":0.8,"status":"active"} +{"schema_version":1,"id":"v060-release-readiness","scope":"project:deja-vu","title":"v0.6.0 release readiness","keywords":["v0.6.0","release","readiness","checklist","candidate"],"aliases":["release-candidate"],"record_path":"memory/summary.md","updated":"2026-08-09","weight":0.4,"status":"superseded","superseded_by":"v070-npm-release-gap"} +{"schema_version":1,"id":"v070-npm-release-gap","scope":"project:deja-vu","title":"Publish and verify the first CLI-capable npm release","keywords":["v0.7.0","npm","publish","npx","latest","cli","release"],"aliases":["npm-release-gap","cli-release"],"record_path":"memory/open-loops/publish-v0.7.0.md","updated":"2026-08-09","weight":0.95,"status":"active"} diff --git a/memory/open-loops/publish-v0.7.0.md b/memory/open-loops/publish-v0.7.0.md new file mode 100644 index 0000000..ea1fd93 --- /dev/null +++ b/memory/open-loops/publish-v0.7.0.md @@ -0,0 +1,33 @@ +--- +id: publish-v0.7.0 +title: Publish and verify Deja Vu v0.7.0 +status: active +scope: project:deja-vu +updated: 2026-08-09 +--- + +# Publish and Verify Deja Vu v0.7.0 + +## Why it matters + +npm `latest` is v0.1.0 and does not include the unified `deja-vu` CLI. The public quick start remains broken until a package containing the current starter kit and binaries is published. + +## Owner + +Repository maintainer. + +## Opened + +2026-08-09. + +## Next trigger + +After the v0.7.0 pull request is reviewed and merged: + +1. run the clean release verification in `docs/release-v0.7.0.md` +2. publish v0.7.0 to npm with public access +3. verify package version, bin metadata, and a fresh `npx ... init --dry-run` +4. create the matching GitHub release +5. update repository metadata and begin the prepared launch sequence + +Do not announce the quick start before the public package smoke test passes. diff --git a/memory/summary.md b/memory/summary.md index e09b26f..8f2c528 100644 --- a/memory/summary.md +++ b/memory/summary.md @@ -3,7 +3,7 @@ id: summary title: Deja Vu Project Summary status: active scope: project:deja-vu -updated: 2026-05-17 +updated: 2026-08-09 --- # Deja Vu Project Summary @@ -24,22 +24,25 @@ Deja Vu provides repo-local, low-cost, human-readable, git-friendly, cross-agent Base use does not require a database, vector store, embeddings, SaaS, daemon, or npm install. -## v0.6.0 focus +## v0.7.0 adoption hardening -v0.6.0 focuses on adoption: +v0.7.0 closes the gap between the repository story and the installable product: -- README landing page -- starter-kit -- per-agent prompts -- `deja-vu init`, `deja-vu doctor`, and `deja-vu explain` -- comparison and compatibility docs -- visual diagram -- launch copy -- demo walkthrough -- release checklist +- README leads with one-command setup and concrete before/after proof. +- `deja-vu init --merge-agents` preserves existing project rules and appends an idempotent Deja Vu block. +- `init` reports incomplete setup and `doctor` rejects rules files that do not actually configure Deja Vu. +- Package verification installs the built tarball and runs its CLI. +- Local Markdown links are validated in CI. + +As of 2026-08-09, npm `latest` is still v0.1.0 and does not expose the unified CLI. Do not launch the `npx @focaxisdev/deja-vu init` path until v0.7.0 is published and verified. + +## Open loops + +- [Publish and verify v0.7.0](./open-loops/publish-v0.7.0.md) ## Boundaries - Say "any capable coding agent can follow the protocol." - Do not overclaim that all agents automatically support Deja Vu. - Do not turn Deja Vu into a heavy memory framework. +- Keep npm publishing, GitHub tagging, and public launch copy behind a verified release checkpoint. diff --git a/package-lock.json b/package-lock.json index 1b11cba..0574c9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@focaxisdev/deja-vu", - "version": "0.6.0", + "version": "0.7.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@focaxisdev/deja-vu", - "version": "0.6.0", + "version": "0.7.0", "license": "MIT", "bin": { "deja-vu": "scripts/deja-vu.mjs", @@ -18,6 +18,9 @@ "@types/node": "^22.15.3", "ts-node": "^10.9.2", "typescript": "^5.8.3" + }, + "engines": { + "node": ">=20" } }, "node_modules/@cspotcode/source-map-support": { diff --git a/package.json b/package.json index 6f86d6c..7ee8033 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@focaxisdev/deja-vu", - "version": "0.6.0", - "description": "Ultra-light repo-local memory protocol for AI coding agents: stop re-explaining your repo to every new chat.", + "version": "0.7.0", + "description": "A 3-file, repo-local memory system for AI coding agents. No database, embeddings, or SaaS.", "type": "module", "main": "./dist/src/index.js", "types": "./dist/src/index.d.ts", @@ -24,6 +24,7 @@ "starter-kit", "scripts", "README.md", + "CONTRIBUTING.md", "LICENSE" ], "scripts": { @@ -31,10 +32,11 @@ "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"", "prepack": "npm run clean && npm run build", "prepublishOnly": "npm test", - "test": "npm run test:src:readonly && npm run build && node --test dist/tests/semantic-recall-engine.test.js dist/tests/memory-cli.test.js && npm run test:pack", + "test": "npm run test:src:readonly && npm run check:links && npm run build && node --test dist/tests/semantic-recall-engine.test.js dist/tests/memory-cli.test.js && npm run test:pack", "test:src": "npm run test:src:readonly", "test:src:readonly": "node --import \"data:text/javascript,import { register } from 'node:module'; import { pathToFileURL } from 'node:url'; register('ts-node/esm', pathToFileURL('./'));\" --test tests/semantic-recall-engine.test.ts tests/memory-cli.test.ts", "test:pack": "node --import \"data:text/javascript,import { register } from 'node:module'; import { pathToFileURL } from 'node:url'; register('ts-node/esm', pathToFileURL('./'));\" --test tests/package-pack.test.ts", + "check:links": "node scripts/check-markdown-links.mjs", "scan:memory": "node scripts/dejavu-scan-memory.mjs", "lint:memory": "node scripts/dejavu-lint-memory.mjs --memory-root examples/protocol-project/memory", "report:feedback": "node scripts/dejavu-feedback-report.mjs --memory-root examples/protocol-project/memory", @@ -80,6 +82,9 @@ }, "author": "Focaxis Dev", "license": "MIT", + "engines": { + "node": ">=20" + }, "devDependencies": { "@types/node": "^22.15.3", "ts-node": "^10.9.2", diff --git a/scripts/check-markdown-links.mjs b/scripts/check-markdown-links.mjs new file mode 100644 index 0000000..d781243 --- /dev/null +++ b/scripts/check-markdown-links.mjs @@ -0,0 +1,60 @@ +#!/usr/bin/env node + +import { existsSync, readFileSync, readdirSync, statSync } from "node:fs"; +import { basename, dirname, isAbsolute, relative, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const ignoredDirectories = new Set([".git", "dist", "node_modules"]); +const markdownFiles = []; + +function collect(path) { + if (!existsSync(path)) return; + const stat = statSync(path); + if (stat.isDirectory()) { + if (ignoredDirectories.has(basename(path))) return; + for (const entry of readdirSync(path)) collect(resolve(path, entry)); + return; + } + if (path.endsWith(".md")) markdownFiles.push(path); +} + +collect(repoRoot); + +const missing = []; +const linkPattern = /!?\[[^\]]*\]\(([^)]+)\)/g; + +for (const file of markdownFiles) { + const text = readFileSync(file, "utf8"); + for (const match of text.matchAll(linkPattern)) { + let target = match[1].trim(); + if (target.startsWith("<") && target.endsWith(">")) target = target.slice(1, -1); + else target = target.split(/\s+["']/u, 1)[0]; + + if (!target || target.startsWith("#") || target.startsWith("//") || /^[a-z][a-z0-9+.-]*:/iu.test(target)) { + continue; + } + + target = target.split(/[?#]/u, 1)[0]; + try { + target = decodeURIComponent(target); + } catch { + missing.push({ file: relative(repoRoot, file), target: match[1], reason: "invalid URL encoding" }); + continue; + } + + const resolved = resolve(dirname(file), target); + const fromRoot = relative(repoRoot, resolved); + if (fromRoot.startsWith("..") || isAbsolute(fromRoot) || !existsSync(resolved)) { + missing.push({ file: relative(repoRoot, file), target: match[1], reason: "target not found" }); + } + } +} + +if (missing.length > 0) { + console.error("Broken local Markdown links:"); + for (const item of missing) console.error(`- ${item.file}: ${item.target} (${item.reason})`); + process.exitCode = 1; +} else { + console.log(`Markdown links: ok (${markdownFiles.length} files checked)`); +} diff --git a/scripts/deja-vu.mjs b/scripts/deja-vu.mjs index de0dda6..4c70bb5 100644 --- a/scripts/deja-vu.mjs +++ b/scripts/deja-vu.mjs @@ -13,6 +13,8 @@ import { fileURLToPath } from "node:url"; const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const starterRoot = resolve(packageRoot, "starter-kit"); const knownAgents = new Set(["codex", "claude-code", "cursor", "windsurf", "chatgpt", "gemini-cli"]); +const rulesStart = ""; +const rulesEnd = ""; const args = process.argv.slice(2); const command = args[0] ?? "help"; @@ -70,6 +72,34 @@ function materializeTemplate(text, projectId) { return text.replaceAll("project:your-project", projectId).replaceAll("2026-05-16", today()); } +function materializeRulesTemplate(projectId) { + return materializeTemplate(readStarter("AGENTS.md"), projectId).replace( + /^Replace `project:[^`]+` with a stable repo-local project id\.\r?\n\r?\n/m, + "", + ); +} + +function hasDejaVuRules(text) { + return text.includes("memory/impressions.jsonl") && text.includes("memory/summary.md"); +} + +function readUtf8ForMerge(path) { + try { + return new TextDecoder("utf-8", { fatal: true }).decode(readFileSync(path)); + } catch { + throw new Error(`Cannot merge Deja Vu rules into ${path}: the file is not valid UTF-8.`); + } +} + +function rulesBlock(projectId, eol = "\n") { + const rules = materializeRulesTemplate(projectId) + .replace(/\r\n/g, "\n") + .replace(/^## /gm, "### ") + .replace(/^# Deja Vu Project Memory Rules/m, "## Deja Vu Project Memory Rules") + .trim(); + return [rulesStart, rules, rulesEnd, ""].join(eol); +} + function getAgents(value) { if (!value) return []; const names = String(value) @@ -105,20 +135,60 @@ function writePlannedFile(plan, targetPath, content, { force, dryRun }) { writeFileSync(targetPath, content, "utf8"); } +function writeAgentsFile(plan, targetPath, content, { force, dryRun, mergeAgents, projectId }) { + if (!existsSync(targetPath)) { + plan.push({ operation: "create", path: targetPath }); + if (!dryRun) writeFileSync(targetPath, content, "utf8"); + return true; + } + + if (force) { + plan.push({ operation: "overwrite", path: targetPath }); + if (!dryRun) writeFileSync(targetPath, content, "utf8"); + return true; + } + + const existing = readUtf8ForMerge(targetPath); + if (hasDejaVuRules(existing)) { + plan.push({ operation: "skip", path: targetPath, reason: "Deja Vu rules already present" }); + return true; + } + + if (!mergeAgents) { + plan.push({ operation: "manual_merge", path: targetPath, reason: "existing AGENTS.md has no Deja Vu rules" }); + return false; + } + + plan.push({ operation: "append", path: targetPath }); + if (!dryRun) { + const eol = existing.includes("\r\n") ? "\r\n" : "\n"; + const separator = existing.endsWith(`${eol}${eol}`) ? "" : existing.endsWith(eol) ? eol : `${eol}${eol}`; + writeFileSync(targetPath, `${existing}${separator}${rulesBlock(projectId, eol)}`, "utf8"); + } + return true; +} + function initCommand(values) { const { options } = parseOptions(values); const cwd = resolve(process.cwd(), String(options.cwd ?? ".")); const dryRun = asBoolean(options.dryRun); const force = asBoolean(options.force); + const mergeAgents = asBoolean(options.mergeAgents); const json = asBoolean(options.json); const projectId = slugifyProjectId(String(options.projectId ?? options.scope ?? basename(cwd))); const agents = getAgents(options.agents ?? options.agent); const plan = []; - writePlannedFile(plan, resolve(cwd, "AGENTS.md"), materializeTemplate(readStarter("AGENTS.md"), projectId), { - dryRun, - force, - }); + if (force && mergeAgents) { + throw new Error("Choose either --force or --merge-agents, not both."); + } + + const rulesReady = writeAgentsFile( + plan, + resolve(cwd, "AGENTS.md"), + materializeRulesTemplate(projectId), + { dryRun, force, mergeAgents, projectId }, + ); writePlannedFile( plan, resolve(cwd, "memory", "summary.md"), @@ -151,17 +221,35 @@ function initCommand(values) { ].join(" "); if (json) { - print({ ok: true, dry_run: dryRun, project_id: projectId, agents, operations: plan, next_prompt: prompt }, true); + print( + { + ok: true, + ready: rulesReady, + dry_run: dryRun, + project_id: projectId, + agents, + operations: plan, + required_action: rulesReady ? null : "Run init again with --merge-agents or merge the starter rules manually.", + next_prompt: prompt, + }, + true, + ); return; } const lines = [ dryRun ? "Deja Vu init dry run:" : "Deja Vu init complete:", ...plan.map((item) => `- ${item.operation}: ${relative(cwd, item.path)}`), - "", - "Next agent prompt:", - prompt, ]; + if (!rulesReady) { + lines.push( + "", + "Action required:", + "- AGENTS.md already exists but does not include Deja Vu rules.", + "- Run `deja-vu init --merge-agents` to append a marked, idempotent rules block, or merge the starter rules manually.", + ); + } + lines.push("", "Next agent prompt:", prompt); print(lines.join("\n"), false); } @@ -248,6 +336,8 @@ function doctorCommand(values) { if (!existsSync(agentsPath)) { addDiagnostic(diagnostics, "error", "Missing AGENTS.md", { path: agentsPath }); + } else if (!hasDejaVuRules(readFileSync(agentsPath, "utf8"))) { + addDiagnostic(diagnostics, "error", "AGENTS.md does not include Deja Vu recall rules", { path: agentsPath }); } if (!existsSync(summaryPath)) { addDiagnostic(diagnostics, "error", "Missing memory/summary.md", { path: summaryPath }); @@ -358,6 +448,7 @@ function help() { "", "Examples:", " deja-vu init --dry-run", + " deja-vu init --merge-agents", " deja-vu init --agents codex,claude-code", " deja-vu doctor --json", " deja-vu explain", diff --git a/starter-kit/README.md b/starter-kit/README.md index 9b7e2a0..38f85a6 100644 --- a/starter-kit/README.md +++ b/starter-kit/README.md @@ -1,11 +1,39 @@ # Deja Vu Starter Kit -Copy this folder into any repo root to add Deja Vu project memory. +Add this starter kit to any repo root to give coding agents durable project memory. + +## Fastest Setup + +The optional CLI creates the starter files without replacing existing files: + +```bash +npx @focaxisdev/deja-vu init +``` + +If the repo already has `AGENTS.md`, preserve it and append a marked, idempotent Deja Vu rules block: + +```bash +npx @focaxisdev/deja-vu init --merge-agents +``` + +## Copy Without npm + +After cloning Deja Vu, copy this directory into the target repo root. + +macOS or Linux: ```bash -cp -R starter-kit/. . +cp -R starter-kit/. /path/to/your-repo/ ``` +PowerShell: + +```powershell +Copy-Item -Path .\starter-kit\* -Destination C:\path\to\your-repo -Recurse -Force +``` + +If the target already has `AGENTS.md`, merge the Deja Vu sections manually instead of replacing its current project rules. + The minimum useful setup is three files: - `AGENTS.md` diff --git a/tests/memory-cli.test.ts b/tests/memory-cli.test.ts index d088378..d679a12 100644 --- a/tests/memory-cli.test.ts +++ b/tests/memory-cli.test.ts @@ -336,6 +336,7 @@ test("unified CLI init dry-run does not create files", () => { const result = runCliJson(["init", "--dry-run", "--project-id", "project:dry-run", "--agents", "codex"], project); assert.equal(result.ok, true); + assert.equal(result.ready, true); assert.equal(result.dry_run, true); assert.equal(result.project_id, "project:dry-run"); assert.ok(result.operations.some((item: { operation: string; path: string }) => item.operation === "create" && item.path.endsWith("AGENTS.md"))); @@ -344,20 +345,45 @@ test("unified CLI init dry-run does not create files", () => { assert.equal(existsSync(join(project, "prompts", "codex.md")), false); }); -test("unified CLI init creates missing files without overwriting existing files", () => { +test("unified CLI init creates memory files and flags existing AGENTS.md for a safe merge", () => { const project = mkdtempSync(join(tmpdir(), "dejavu-init-")); writeFileSync(join(project, "AGENTS.md"), "# Existing rules\n", "utf8"); const result = runCliJson(["init", "--project-id", "project:sample", "--agents", "codex"], project); assert.equal(result.ok, true); - assert.ok(result.operations.some((item: { operation: string; path: string }) => item.operation === "skip" && item.path.endsWith("AGENTS.md"))); + assert.equal(result.ready, false); + assert.ok(result.required_action.includes("--merge-agents")); + assert.ok(result.operations.some((item: { operation: string; path: string }) => item.operation === "manual_merge" && item.path.endsWith("AGENTS.md"))); assert.equal(readFileSync(join(project, "AGENTS.md"), "utf8"), "# Existing rules\n"); assert.ok(readFileSync(join(project, "memory", "summary.md"), "utf8").includes("scope: project:sample")); assert.ok(readFileSync(join(project, "memory", "impressions.jsonl"), "utf8").includes('"scope":"project:sample"')); assert.equal(existsSync(join(project, "prompts", "codex.md")), true); }); +test("unified CLI init can append an idempotent rules block to existing AGENTS.md", () => { + const project = mkdtempSync(join(tmpdir(), "dejavu-init-merge-")); + const agentsPath = join(project, "AGENTS.md"); + writeFileSync(agentsPath, "# Existing rules\n\nKeep this line.\n", "utf8"); + + const first = runCliJson(["init", "--project-id", "project:sample", "--merge-agents"], project); + const afterFirst = readFileSync(agentsPath, "utf8"); + const second = runCliJson(["init", "--project-id", "project:sample", "--merge-agents"], project); + const afterSecond = readFileSync(agentsPath, "utf8"); + + assert.equal(first.ready, true); + assert.ok(first.operations.some((item: { operation: string; path: string }) => item.operation === "append" && item.path.endsWith("AGENTS.md"))); + assert.ok(afterFirst.startsWith("# Existing rules\n\nKeep this line.\n")); + assert.ok(afterFirst.includes("")); + assert.ok(afterFirst.includes("Scope: `project:sample`")); + assert.ok(afterFirst.includes("### Memory Identity")); + assert.ok(!afterFirst.includes("Replace `project:sample`")); + assert.equal(afterFirst.match(//g)?.length, 1); + assert.equal(second.ready, true); + assert.ok(second.operations.some((item: { operation: string; path: string }) => item.operation === "skip" && item.path.endsWith("AGENTS.md"))); + assert.equal(afterSecond, afterFirst); +}); + test("unified CLI doctor reports missing required files", () => { const project = mkdtempSync(join(tmpdir(), "dejavu-doctor-missing-")); @@ -370,6 +396,33 @@ test("unified CLI doctor reports missing required files", () => { assert.ok(result.diagnostics.some((item: { message: string }) => item.message === "Missing memory/impressions.jsonl")); }); +test("unified CLI doctor rejects an AGENTS.md without Deja Vu recall rules", () => { + const project = mkdtempSync(join(tmpdir(), "dejavu-doctor-rules-")); + const memory = join(project, "memory"); + mkdirSync(memory); + writeFileSync(join(project, "AGENTS.md"), "# Existing rules\n", "utf8"); + writeFileSync(join(memory, "summary.md"), "# Summary\n", "utf8"); + writeFileSync( + join(memory, "impressions.jsonl"), + `${JSON.stringify({ + schema_version: 1, + id: "summary", + scope: "project:test", + title: "Summary", + keywords: ["summary", "project", "constraints"], + record_path: "memory/summary.md", + updated: "2026-05-16", + status: "active", + })}\n`, + "utf8", + ); + + const result = runCliJsonAllowFailure(["doctor"], project); + + assert.equal(result.ok, false); + assert.ok(result.diagnostics.some((item: { message: string }) => item.message === "AGENTS.md does not include Deja Vu recall rules")); +}); + test("unified CLI doctor catches unresolved feedback and obvious secrets", () => { const project = mkdtempSync(join(tmpdir(), "dejavu-doctor-risk-")); const memory = join(project, "memory"); diff --git a/tests/package-pack.test.ts b/tests/package-pack.test.ts index febaa00..a0bda30 100644 --- a/tests/package-pack.test.ts +++ b/tests/package-pack.test.ts @@ -1,15 +1,30 @@ import test from "node:test"; import assert from "node:assert/strict"; -import { execSync } from "node:child_process"; +import { execFileSync } from "node:child_process"; +import { mkdtempSync, mkdirSync, readFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; const root = process.cwd(); test("package metadata exposes memory CLI binaries and starter kit", () => { - const result = JSON.parse(execSync("npm pack --dry-run --json", { cwd: root, encoding: "utf8" })); + const npmExecPath = process.env.npm_execpath; + assert.ok(npmExecPath, "npm_execpath must be available when this test runs through npm"); + const packRoot = mkdtempSync(join(tmpdir(), "dejavu-pack-")); + const cache = join(packRoot, "npm-cache"); + const result = JSON.parse( + execFileSync(process.execPath, [npmExecPath, "pack", "--json", "--pack-destination", packRoot], { + cwd: root, + encoding: "utf8", + env: { ...process.env, npm_config_cache: cache }, + }), + ); const files = new Set(result[0].files.map((file: { path: string }) => file.path)); assert.equal(result[0].name, "@focaxisdev/deja-vu"); + assert.equal(result[0].version, "0.7.0"); assert.ok(files.has("scripts/deja-vu.mjs")); + assert.ok(files.has("scripts/check-markdown-links.mjs")); assert.ok(files.has("scripts/dejavu-scan-memory.mjs")); assert.ok(files.has("scripts/dejavu-lint-memory.mjs")); assert.ok(files.has("scripts/dejavu-feedback-report.mjs")); @@ -17,4 +32,24 @@ test("package metadata exposes memory CLI binaries and starter kit", () => { assert.ok(files.has("starter-kit/memory/summary.md")); assert.ok(files.has("starter-kit/memory/impressions.jsonl")); assert.ok(files.has("starter-kit/prompts/codex.md")); + assert.ok(files.has("docs/assets/deja-vu-preview.svg")); + assert.ok(files.has("CONTRIBUTING.md")); + + const installRoot = join(packRoot, "install"); + mkdirSync(installRoot); + const tarball = join(packRoot, result[0].filename); + execFileSync( + process.execPath, + [npmExecPath, "install", "--prefix", installRoot, "--ignore-scripts", "--no-audit", "--no-fund", tarball], + { cwd: packRoot, encoding: "utf8", env: { ...process.env, npm_config_cache: cache } }, + ); + + const installedRoot = join(installRoot, "node_modules", "@focaxisdev", "deja-vu"); + const installedManifest = JSON.parse(readFileSync(join(installedRoot, "package.json"), "utf8")); + assert.equal(installedManifest.bin["deja-vu"], "scripts/deja-vu.mjs"); + const help = execFileSync(process.execPath, [join(installedRoot, "scripts", "deja-vu.mjs"), "--help"], { + cwd: installRoot, + encoding: "utf8", + }); + assert.ok(help.includes("deja-vu init --merge-agents")); });