RuleStack

Configs

Stacks

Compare

Diff

RuleStack

Configs

Stacks

Compare

Diff

Read API

RuleStack

Configs

Stacks

Compare

Diff

Read API

Diff/pnpm-pnpm-agents ↔ pnpm-pnpm-pnpr-agents

Comparison

A · AGENTS.md · pnpm/pnpmB · AGENTS.md · pnpm/pnpm
What each file covers, counted
DimensionSharedOnly in AOnly in BOverlap
Sections13083%
Commands12663%
Section tags57138%

What each file covers

Sections

1 shared · 30 only in A · 8 only in B
  • − Agent Guide to pnpm Repository
  • − Keep pnpm and pacquet in sync
  • − Repository Structure
  • − Core Directories
  • − CLI & Configuration
  • − Other Functional Directories
  • − Shared Utilities
  • − Rust Projects
  • − Setup & Build (TypeScript only)
  • − Testing (TypeScript only)
  • − From the project directory
  • − From the root, filtering by package name
  • − Linting (TypeScript only)
  • − Never ignore test failures
  • − AI Review Guidance
  • − Code Reuse and Avoiding Duplication
  • − Commit Messages
  • − Install the git hooks before committing
  • − or, if dependencies are already installed, register the hooks on their own:
  • − Never use bare `#NNN` issue/PR references
  • − Never use a bare `@mention`
  • − Changesets
  • − Changesets for the Rust products
  • − Code Style (TypeScript only)
  • − Conventions
  • − Common Gotchas
  • − Error Type Checking in Jest (TypeScript only)
  • − Working with GitHub PRs, Issues, and Comments
  • − Resolving Conflicts in GitHub PRs
  • − Key Configuration Files
  • + AGENTS.md (pnpr)
  • + What this project is
  • + Relationship to pacquet
  • + Layout
  • + Code reuse
  • + New registry-only crates
  • + Dependencies
  • + Style, tests, commits
  •   Comments

Commands

1 shared · 26 only in A · 6 only in B
  • − pnpm install
  • − pnpm run compile
  • − pnpm --filter <package_name> run compile
  • − pnpm --filter pnpm run compile
  • − pnpm/test/
  • − pnpm --filter <package_name> test
  • − pnpm --filter <package_name> test <file_path>
  • − pnpm --filter <package_name> test <file_path> -t <test_name_pattern>
  • − pnpm run lint
  • − pnpm exec husky
  • − pnpm/AGENTS.md
  • − pnpm/dist/pnpm.mjs
  • − pnpm run bundle
  • − git config core.hooksPath
  • − pnpm/pnpm#NNN
  • − git commit --no-verify
  • − pnpm change
  • − pnpm change status
  • − pnpm version -r
  • − pnpm-workspace.yaml
  • − pnpm lane main --filter …
  • − pnpm add
  • − git log
  • − git blame
  • − gh pr create
  • − pnpm-lock.yaml
  • + just check
  • + just test
  • + just lint
  • + just fmt
  • + cargo
  • + just
  •   pnpm

Section tags

5 shared · 7 only in A · 1 only in B
  • − setup
  • − build
  • − test
  • − types
  • − performance
  • − do-not
  • − agent-behaviour
  • + lint-format
  •   code-style
  •   architecture
  •   git-pr
  •   dependencies
  •   docs

Line diff

+85 added−304 removed30 unchanged9.0% identical
pnpm/pnpm · AGENTS.md
@@ −1 @@
1# Agent Guide to pnpm Repository
2 
3This document provides context and instructions for AI agents working on the pnpm codebase.
4 
5The repository contains three products:
 
 
6 
7- The **TypeScript pnpm CLI** — the main TypeScript workspaces outside `pnpm/` and `pnpr/`.
8- The **Rust pacquet port** — `pnpm/`. See [`pnpm/AGENTS.md`](./pnpm/AGENTS.md) for pacquet-specific rules; it adds to (and never contradicts) the conventions below.
9- The **Rust pnpr registry server** — `pnpr/`. See [`pnpr/AGENTS.md`](./pnpr/AGENTS.md) for pnpr-specific rules; it adds to (and never contradicts) the conventions below.
10 
11Sections below marked "(TypeScript only)" apply to TypeScript code only; they do not apply to Rust code in `pnpm/` or `pnpr/`. Everything else applies repo-wide unless a nested `AGENTS.md` specializes it.
 
 
12 
13## Keep pnpm and pacquet in sync
 
 
 
14 
15The two stacks are parallel implementations of the same CLI, kept behaviorally identical — the same flags, defaults, error codes, file formats, and lockfile shape. They are now at near-complete feature parity and are developed together, so **any user-visible change has to land in both at the same time.** Neither stack is downstream of the other: pacquet is a source of truth in its own right, not a port that trails the TypeScript CLI.
16 
17When you change one side, do the equivalent change on the other in the same PR if you can. If you can't (different expertise, scope too large, or pacquet hasn't ported the surrounding feature yet), open the PR with just your side — call out in the description what still needs porting, and someone else will push the matching commits to the same PR before it lands.
 
 
 
18 
19"User-visible" means anything that affects the CLI surface or the on-disk contract: command-line flags and defaults, environment-variable handling, lockfile/manifest/state-file format, error codes and messages, log emissions parsed by `@pnpm/cli.default-reporter`, store layout, hook semantics. Pure internal refactors, perf wins, and TS-only test cleanups don't need mirroring.
 
 
 
20 
21**Any user-visible change to either stack must be replicated in the other.**
22 
23The pacquet-side conventions for keeping the two stacks aligned are in [`pnpm/AGENTS.md`](./pnpm/AGENTS.md#the-cardinal-rule).
24 
25## Repository Structure
26 
27The pnpm codebase is a monorepo managed by pnpm itself. The root contains functional directories organized by domain:
28 
29### Core Directories
30 
31- `pnpm/`: The CLI entry point and main package.
32- `pkg-manager/`: Core package management logic (installation, linking, etc.).
33- `resolving/`: Dependency resolution logic (resolvers for npm, tarballs, git, etc.).
34- `fetching/`: Package fetching logic.
35- `store/`: Store management logic (content-addressable storage).
36- `lockfile/`: Lockfile handling, parsing, and utilities.
37 
38### CLI & Configuration
39 
40- `cli/`: CLI command implementations and infrastructure.
41- `config/`: Configuration management and parsing.
42- `hooks/`: pnpm hooks (readPackage, etc.).
43- `completion/`: Shell completion support.
44 
45### Other Functional Directories
46 
47- `network/`: Network-related utilities (proxy, fetch, auth).
48- `workspace/`: Workspace-related utilities.
49- `exec/`: Execution-related commands (run, exec, dlx).
50- `env/`: Node.js environment management.
51- `cache/`: Cache-related commands and utilities.
52- `patching/`: Package patching functionality.
53- `reviewing/`: License and dependency review tools.
54- `releasing/`: Release and publishing utilities.
55 
56### Shared Utilities
57 
58- `packages/`: Shared utility packages (constants, error handling, logger, types, etc.).
59- `fs/`: Filesystem utilities.
60- `crypto/`: Cryptographic utilities.
61- `text/`: Text processing utilities.
62 
63### Rust Projects
64 
65- `pnpm/`: The pnpm CLI ported to Rust. Self-contained sub-project with its own crates, tests, and tooling — see [`pnpm/AGENTS.md`](./pnpm/AGENTS.md).
66- `pnpr/`: The pnpm-compatible npm registry server. Self-contained sub-project with its own crates, tests, and tooling — see [`pnpr/AGENTS.md`](./pnpr/AGENTS.md).
67 
68## Setup & Build (TypeScript only)
69 
70To set up the environment and build the project:
71 
72```bash
73pnpm install
74pnpm run compile
75```
76 
77To compile a specific package:
 
 
78 
79```bash
80pnpm --filter <package_name> run compile
81```
82 
83**Important:** The pnpm CLI e2e tests (in `pnpm/test/`) use the **bundled** `pnpm/dist/pnpm.mjs`, not the individual package `lib/` outputs. After changing any package, you must rebuild the bundle before running e2e tests:
 
 
 
 
 
 
 
 
84 
85```bash
86pnpm --filter pnpm run compile
87```
 
88 
89This runs `tsgo --build`, linting, and `pnpm run bundle` (which bundles all packages into `pnpm/dist/pnpm.mjs`). Without this step, e2e tests will use a stale bundle and your changes won't be tested.
90 
91## Testing (TypeScript only)
 
 
 
 
 
 
 
92 
93Never run all tests in the repository as it takes a lot of time.
 
94 
95Run tests for a specific project instead:
96 
97```bash
98# From the project directory
99pnpm test
 
100 
101# From the root, filtering by package name
102pnpm --filter <package_name> test
103```
104 
105Or better yet, run tests for a specific file:
 
 
 
 
 
106 
107```bash
108pnpm --filter <package_name> test <file_path>
109```
110 
111Or a specific test case in a specific file:
112 
113```bash
114pnpm --filter <package_name> test <file_path> -t <test_name_pattern>
115```
116 
117## Linting (TypeScript only)
118 
119To run all linting checks:
120 
121```bash
122pnpm run lint
 
123```
124 
125## Never ignore test failures
126 
127Do not dismiss a failing test as a "pre-existing" failure that is unrelated to your changes. Every test failure must be investigated and fixed. If a test was already broken before your changes, fix it as part of your work — do not silently skip it or treat it as acceptable.
128 
129## AI Review Guidance
130 
131The repository's review framework lives in **[REVIEW_GUIDE.md](./REVIEW_GUIDE.md)** — how changes are accepted or rejected, the security-first / performance-second priorities, the security checklist and advisory regression themes, and the test/changeset/parity expectations. Apply it when reviewing pull requests. (TypeScript-specific code style and engineering conventions for the CLI are documented in the "Code Style" section of this file; pacquet and pnpr follow their own `AGENTS.md` and style guides.)
132 
133Security is the first review priority and performance the second. Surface only issues tied to the changed code, and explain the exploit path, impact, or hot path affected. See the guide's Security and Performance review sections for the full checklist.
134 
135## Code Reuse and Avoiding Duplication
136 
137**Before writing new code, always analyze the existing codebase for similar functionality.** This is a large monorepo with many shared utilities — duplication is a real risk.
138 
139- **Search before you write.** Before implementing any non-trivial logic, search the codebase for existing functions, utilities, or patterns that do the same or similar thing. Check `packages/`, `fs/`, `crypto/`, `text/`, and other shared directories first.
140- **Extract shared code.** If you find that the logic you need already exists in another package but is not exported or reusable, refactor it into a shared package rather than duplicating it. If you are adding new code that is similar to code that already exists elsewhere in the repo, move the common parts into a shared package that both locations can use.
141- **Prefer open source packages over custom implementations.** Do not reimplement functionality that is already available as a well-maintained open source package. Use established libraries for common tasks (e.g., path manipulation, string utilities, data structures, schema validation). Only write custom code when no suitable package exists or when the existing packages are too heavy or unmaintained.
142- **Keep the dependency on the right level.** When adding a new open source dependency, add it to the most specific package that needs it, not to the root or to a shared package unless multiple packages depend on it.
143 
144## Commit Messages
145 
146Follow the [Conventional Commits](https://www.conventionalcommits.org/) specification.
147 
148- `feat`: a new feature
149- `fix`: a bug fix
150- `docs`: documentation only changes
151- `style`: formatting, missing semi-colons, etc.
152- `refactor`: code change that neither fixes a bug nor adds a feature
153- `perf`: a code change that improves performance
154- `test`: adding missing tests
155- `chore`: changes to build process or auxiliary tools
156 
157### Install the git hooks before committing
158 
159The git hooks in `.husky/` (including the `commit-msg` check described below) only run once husky has wired them into git. A fresh clone does **not** have them active until installed. **Before making any commit, ensure the hooks are installed** by running one of:
160 
161```bash
162pnpm install # runs the "prepare": "husky" script as part of install
163# or, if dependencies are already installed, register the hooks on their own:
164pnpm exec husky
165```
166 
167You can confirm the hooks are active with `git config core.hooksPath` (it should point at husky's directory) and by checking that `.husky/_/` exists. Do not commit with hooks uninstalled — that silently skips every check, including the bare `#NNN` rejection below.
168 
169### Never use bare `#NNN` issue/PR references
170 
171**Do not write a bare `#NNN` (a `#` followed by digits) anywhere in a commit message.** A `commit-msg` hook (`.husky/reject-bare-issue-refs.mjs`) rejects them.
172 
173GitHub turns any `#NNN` into a link to issue/PR `NNN` of *this* repo, which is almost never what a bare reference means. This is a frequent AI mistake in two forms:
174 
175- Using `#1`, `#2`, `#3`, … to enumerate items in a list. GitHub instead links them to unrelated issues `#1`, `#2`, `#3` of this repo. **Fix:** don't use `#` for enumeration — write `item 1`, `(1)`, `1.`, or rephrase.
176- Referring to issue `#NNN` of a *different* repository. GitHub instead links it to issue `NNN` of this repo. **Fix:** use qualified syntax `owner/repo#NNN` or an absolute URL `https://github.com/owner/repo/issues/NNN`.
177 
178For references to issues/PRs in **this** repo, also use the qualified form `pnpm/pnpm#NNN` or the absolute URL `https://github.com/pnpm/pnpm/issues/NNN`. Qualified syntax and absolute URLs are always unambiguous, so this rule is applied to every `#NNN` without exception.
179 
180**Address the root cause when the hook fires.** Rewrite the reference into the correct unambiguous form. Never bypass the check with `git commit --no-verify`, by editing or deleting the hook, or with any suppression file.
181 
182### Never use a bare `@mention`
183 
184**Do not write a bare `@name` (an `@` followed by a username-like token) anywhere in a commit message.** A `commit-msg` hook (`.husky/reject-bare-mentions.mjs`) rejects them.
185 
186GitHub turns any `@name` into a mention of that user/org/team, which is wrong either way it is meant:
187 
188- If it is code (a scoped package like `@pnpm/core`, a handle, a path), GitHub should not treat it as a mention.
189- If it really is a person, every push, force-push, and rebase that carries the commit re-notifies them — noise nobody asked for.
190 
191**Fix:** wrap the reference in backticks so GitHub renders it as code and sends no notification — e.g. `` `@pnpm/core` `` or `` `@foo` `` — or remove it if it is not needed. Never bypass the check with `git commit --no-verify`, by editing or deleting the hook, or with any suppression file.
192 
193## Changesets
194 
195If your changes affect published packages, you MUST create a changeset file in the `.changeset` directory (`pnpm change` records one interactively; `pnpm change status` shows the pending release plan). The file describes the change and specifies the affected packages with their pending version bump types: patch, minor, or major. Write the description for pnpm users and keep it concise — it becomes a release note. Implementation rationale belongs in the commit message, not the changeset. The bare `pnpm version -r` consumes the pending changesets at release time; there is no separate `@changesets/cli` dependency.
196 
197**IMPORTANT: Always explicitly include `"pnpm"` in the changeset** with the appropriate version bump (patch, minor, or major). The pnpm CLI will only receive automatic patch bumps from its dependencies, so if your change warrants a minor or major version bump for the CLI, you must specify it explicitly. The changeset description will appear on the release notes page.
198 
199Example:
200 
201```
202---
203"@pnpm/installing.deps-installer": minor
204"pnpm": minor
205---
206 
207Added a new setting `blockExoticSubdeps` that prevents the resolution of exotic protocols in transitive dependencies [#10352](https://github.com/pnpm/pnpm/issues/10352).
208```
209 
210**Versioning Guidelines for pnpm CLI:**
211- **patch**: Bug fixes, internal refactors, and changes that don't require documentation updates
212- **minor**: New features, settings, or commands that should be documented (anything users should know about)
213- **major**: Breaking changes
214 
215### Changesets for the Rust products
216 
217The Rust products are released through the same native flow. Their npm wrapper packages are workspace packages with committed versions, so a user-visible change to a Rust product needs a changeset too, targeting:
218 
219- `pacquet` — the Rust pnpm CLI (published to npm as `pnpm` and `@pnpm/exe` under its `next-<major>` dist-tag; named `pacquet` in-repo so its name can't collide with the TypeScript CLI). `@pnpm/napi` is a `versioning.fixed` group with it and bumps with it automatically.
220- `@pnpm/napi` — the Node.js addon bindings for the Rust engine.
221- `@pnpm/pnpr` — the pnpr registry server (published as `@pnpm/pnpr` and its platform packages, plus the `ghcr.io/pnpm/pnpr` Docker image).
222 
223The Rust products release on `alpha` lanes (`versioning.lanes` in `pnpm-workspace.yaml`): each run of `pnpm version -r` that consumes an intent for one of them cuts an `X.Y.Z-alpha.N` prerelease, while the TypeScript CLI keeps releasing stable versions on the main lane. `pnpm lane main --filter …` graduates a product to a stable version.
224 
225Do not add `"pnpm"` to a Rust-only changeset: in changesets, `pnpm` always means the TypeScript CLI package. A changeset whose implementation is Rust-only and targets `pacquet` must omit `"pnpm"`. A parity change that lands in both stacks carries one changeset naming both the affected TypeScript packages (plus `"pnpm"`) and the Rust wrapper(s).
226 
227Use `pacquet` as the changeset package name, but use `pnpm` in its release-note prose and command examples (`pnpm add`, not `pacquet add`). The published Rust CLI's executable is `pnpm`; `pacquet` is only its in-repo package identifier.
228 
229## Comments
230 
231These conventions apply to the TypeScript pnpm CLI, pacquet, and pnpr. Product-specific `AGENTS.md` files may add language-specific rules, but they do not weaken this baseline.
232 
233Write code that explains itself. A reader should understand what a function does from its name, parameters, and types — not from prose above the call site.
234 
235Defaults:
236 
237- **Do not write a comment** that restates what the code already says. If renaming a variable, splitting a helper, or moving a check to a more obvious place would carry the information, do that instead.
238- **Do not repeat documentation** at call sites that already lives on the callee. If the function has JSDoc, a Rust doc comment, or equivalent API documentation, the call site shouldn't re-explain what calling it does. Update the documentation once; let every call site benefit.
239- **Put a shared *why* in one place.** When the same rationale underlies several related functions — peers that delegate to a common helper, or a type and its methods — document it once at that common home and reference it from the rest, instead of re-deriving it in each. This is the call-site rule applied sideways across peers, not just upward to a callee.
240- **Documentation comments are for the item's contract** — preconditions, postconditions, edge cases, why the item exists. Not for re-narrating the body.
241- **Do not record past implementation shape, refactor history, or "the previous code did X" framing.** That's what `git log` and `git blame` are for. Describe the current contract — what the code is and what it guarantees — not what it replaced. Phrasings like "used to", "previously", "the original X", or a parenthetical naming a removed type belong in the commit message, not in the source.
242 
243Write a comment only when:
244 
245- The reason for the code is non-obvious from reading it (a hidden invariant, a workaround for a known bug, a deliberate exception to the surrounding pattern).
246- The right name doesn't fit — e.g., a temporary technical constraint that's worth flagging but doesn't justify a new symbol.
247 
248Before adding a comment, ask: "Could I rename, restructure, or extract instead?" If yes, do that. The bar for prose-in-code is high; the bar for prose-that-restates-code is "don't."
249 
250## Code Style (TypeScript only)
251 
252This repository uses [Standard Style](https://github.com/standard/standard) with a few modifications:
253- **Trailing commas** are used.
254- **Functions are preferred** over classes.
255- **Functions are declared after they are used** (hoisting is relied upon).
256- **Functions should have no more than two or three arguments.** If a function needs more parameters, use a single options object instead.
257- **Import Order**:
258 1. Standard libraries (e.g., `fs`, `path`).
259 2. External dependencies (sorted alphabetically).
260 3. Relative imports.
261 
262To ensure your code adheres to the style guide, run:
263 
264```bash
265pnpm run lint
266```
267 
268### Conventions
269 
270Recurring engineering conventions in this codebase — the rules reviewers most often enforce:
271 
272- **Errors.** Throw `PnpmError` (from `@pnpm/error`) for user-reachable errors — they are part of the UX and carry a stable code. Programmer-error, type-guard, and unreachable-branch errors stay plain `Error`. Never swallow errors; catch only the specific expected code (not "any error" when you meant `ENOENT`). Throw on impossible states rather than continuing. Error messages must carry context, e.g. the offending path.
273- **Naming.** Functions are verbs; types and fields are specific, not generic. Reuse existing terminology rather than inventing synonyms. File names follow the existing convention; rename a concept everywhere it appears.
274- **Reuse repo libraries.** Don't add a dependency, or hand-roll logic, for a job an existing repo utility or an already-present library does — search for it first. Deduplicate copy-pasted logic into a shared function or package.
275- **String parsing.** Prefer plain string operations over a custom regular expression. When the input needs structured parsing with backtracking, use the existing parser-combinator pattern (`object/property-path`).
276- **Dependency placement.** Shared infrastructure (the logger, etc.) is a peer dependency. (The narrowest-package rule is covered under "Code Reuse and Avoiding Duplication" above.)
277- **Config and layering.** Configurable values flow through `@pnpm/config` and reach commands via options — don't hardcode them (CLI options are camelCased automatically). Command handlers return data and let the CLI print it, which keeps them unit-testable. Don't add a wrapper function that adds nothing.
278- **Async and loops.** Prefer async fs and `async/await`; run independent work with `Promise.all`/`Promise.any` and `await` what must complete; hoist invariant work out of loops.
279 
280## Common Gotchas
281 
282### Error Type Checking in Jest (TypeScript only)
283 
284When checking if a caught error is an `Error` object, **do not use `instanceof Error`**. Jest runs tests in a VM context where `instanceof` checks can fail across realms.
285 
286Instead, use `util.types.isNativeError()`:
287 
288```typescript
289import util from 'util'
290 
291try {
292 // ... some operation
293} catch (err: unknown) {
294 // ❌ Wrong - may fail in Jest
295 if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {
296 return null
297 }
298
299 // ✅ Correct - works across realms
300 if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
301 return null
302 }
303 throw err
304}
305```
306 
307## Working with GitHub PRs, Issues, and Comments
308 
309- **Open every PR with the repository template.** `gh pr create` does not apply `.github/pull_request_template.md` automatically, so read that file and pass its filled-in contents as the PR body (`--body`/`--body-file`). Keep every section (Summary, Squash Commit Body, Checklist), fill them in for this change, mark the checklist items, and remove only the lines the template says are inapplicable.
310- **Keep PR titles and descriptions current.** When pushing new changes to a PR, review the title and description and update them if they no longer accurately reflect what the PR does.
311- **Reply to and resolve review conversations.** Once a review comment has been addressed, reply to the thread with a description of the resolution including the commit hash that fixed it, then mark the conversation as resolved.
312- **Sign all agent-authored content.** When posting a comment, creating an issue, or opening a PR, append a footer to the message indicating that it was written by an agent. The footer must include the name of the agent and the name of the model used. Example:
313 
314 ```markdown
315 ---
316 Written by an agent (Claude Code, claude-opus-4-7).
317 ```
318 
319## Resolving Conflicts in GitHub PRs
320 
321Use `shell/resolve-pr-conflicts.sh` to resolve PR conflicts:
322 
323```bash
324./shell/resolve-pr-conflicts.sh <PR_NUMBER>
325```
326 
327The script force-fetches the base branch (avoiding stale refs), rebases, auto-resolves `pnpm-lock.yaml` conflicts via `pnpm install`, force-pushes, and verifies GitHub sees the PR as mergeable. For non-lockfile conflicts it will pause and list the files that need manual resolution.
328 
329## Key Configuration Files
330 
331- `pnpm-workspace.yaml`: Defines the workspace structure.
332- `package.json` (root): Root scripts and devDependencies.
333- `CONTRIBUTING.md`: Detailed contribution guidelines.
334 
pnpm/pnpm · pnpr/AGENTS.md
@@ +1 @@
1# AGENTS.md (pnpr)
2 
3Guidance for AI coding agents working in `pnpr/`.
4 
5**Read [`../AGENTS.md`](../AGENTS.md) first.** It covers the monorepo-wide
6conventions: GitHub PR workflow, signing agent-authored content, conventional
7commit messages, code-reuse philosophy, and "never ignore test failures."
8 
9## What this project is
 
 
10 
11`pnpr/` is a pnpm-compatible npm registry server written in Rust —
12roughly the role [verdaccio](https://verdaccio.org/) plays in the JS
13ecosystem. It is a **sibling** of `pnpm/`, not part of it.
14 
15The two Rust projects share the same Cargo workspace at the repo root so
16that the registry can depend directly on `pacquet-*` crates (tarball
17handling, integrity hashes, manifest parsing, network plumbing, etc.) and
18the `Cargo.lock` stays unified.
19 
20## Relationship to pacquet
21 
22- **`pnpm/`** is a *port* of the pnpm CLI. Its cardinal rule is
23 "match pnpm exactly" — see [`../pnpm/AGENTS.md`](../pnpm/AGENTS.md).
24- **`pnpr/`** has no pnpm-CLI counterpart to mirror. It is a new
25 server. Behavior here is designed, not ported.
26 
27That means the "match upstream pnpm" discipline that governs `pnpm/`
28does **not** apply here. The registry can pick its own architecture,
29flags, and config format. It must still be compatible with the npm
30registry protocol that pnpm (and npm, yarn, etc.) clients speak.
31 
32## Layout
33 
34Mirrors `pnpm/`:
35 
36```text
37pnpr/
38 AGENTS.md
39 crates/
40 pnpr/ -> package "pnpr"
41 Cargo.toml
42 README.md
43 src/
44 lib.rs -> library API
45 main.rs -> binary entry point (ships the `pnpr` binary)
46 # future sibling crates land here, see "New registry-only crates" below
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47```
48 
49The Rust workspace itself, `rust-toolchain.toml`, `justfile`, and
50`Cargo.lock` live at the **repo root** — run `cargo` and `just` from there.
51`pnpr/crates/*` is wired into the root workspace `members`.
52 
53## Code reuse
 
 
54 
55**Prefer existing `pacquet-*` crates over writing new code.** Before
56implementing anything non-trivial, check whether `pacquet-*` already
57solves it. Candidates worth checking first: `pacquet-tarball`,
58`pacquet-crypto-hash`, `pacquet-crypto-shasums-file`,
59`pacquet-package-manifest`, `pacquet-network`, `pacquet-registry`,
60`pacquet-fs`, `pacquet-diagnostics`. Add a `pacquet-*` crate the same
61way pacquet crates do: declare it in the root `[workspace.dependencies]`
62(already done for the pacquet crates) and use `{ workspace = true }`
63in this crate's `Cargo.toml`.
64 
65If a piece of code currently inside `pnpm/` turns out to be genuinely
66shared between the two stacks and the `pacquet-` prefix becomes
67misleading, propose renaming/relocating it in a dedicated PR — not as a
68drive-by during feature work.
69 
70### New registry-only crates
71 
72When the registry needs its own crate (logic that isn't shared with the
73pnpm port and doesn't fit in `pnpm/`), put it under
74`pnpr/crates/<short-name>/` and name the package
75`pnpr-<short-name>` in its `Cargo.toml`. The
76`pnpr/crates/*` glob in the root workspace `members` picks it up
77automatically; just add the new crate to `[workspace.dependencies]` at
78the root with the `pnpr-` prefix so other crates can use
79`{ workspace = true }`.
80 
81Use the `pnpr-` prefix exclusively for registry-only crates.
82Don't reach for `pacquet-` to name something new on the registry side.
83 
84## Dependencies
85 
86Same rule as pacquet: a dependency that is already declared in
87`[workspace.dependencies]` may be used by any crate that needs it.
88Adding a new third-party crate to the workspace requires an explicit
89human request (see [`../pnpm/AGENTS.md`](../pnpm/AGENTS.md#things-not-to-do)).
90 
91## Style, tests, commits
 
 
92 
93Follow the pacquet code-style guide
94([`../pnpm/CODE_STYLE_GUIDE.md`](../pnpm/CODE_STYLE_GUIDE.md)) and the
95pacquet contributing guide ([`../pnpm/CONTRIBUTING.md`](../pnpm/CONTRIBUTING.md))
96for Rust-level conventions — imports, naming, ownership, error handling,
97test layout. They are written for pacquet but apply to any Rust code in
98this workspace.
99 
100### Comments
 
 
101 
102Follow the repo-wide comment baseline in [`../AGENTS.md`](../AGENTS.md#comments) and the Rust-specific additions in [`../pnpm/AGENTS.md`](../pnpm/AGENTS.md#comments).
103 
104Commit messages use Conventional Commits with `pnpr` as the scope
105(`feat(pnpr): ...`, `fix(pnpr): ...`).
 
106 
107Run the same checks pacquet does before declaring work done:
108 
109```sh
110just check # cargo check --locked --workspace --all-targets
111just test # cargo nextest run
112just lint # cargo clippy --workspace --all-targets -- --deny warnings
113just fmt # cargo fmt + taplo format
114```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115 
@@ −1 +1 @@
1−# Agent Guide to pnpm Repository
1+# AGENTS.md (pnpr)
22  
3−This document provides context and instructions for AI agents working on the pnpm codebase.
3+Guidance for AI coding agents working in `pnpr/`.
44  
5−The repository contains three products:
5+**Read [`../AGENTS.md`](../AGENTS.md) first.** It covers the monorepo-wide
6+conventions: GitHub PR workflow, signing agent-authored content, conventional
7+commit messages, code-reuse philosophy, and "never ignore test failures."
68  
7−- The **TypeScript pnpm CLI** — the main TypeScript workspaces outside `pnpm/` and `pnpr/`.
8−- The **Rust pacquet port** — `pnpm/`. See [`pnpm/AGENTS.md`](./pnpm/AGENTS.md) for pacquet-specific rules; it adds to (and never contradicts) the conventions below.
9−- The **Rust pnpr registry server** — `pnpr/`. See [`pnpr/AGENTS.md`](./pnpr/AGENTS.md) for pnpr-specific rules; it adds to (and never contradicts) the conventions below.
9+## What this project is
1010  
11−Sections below marked "(TypeScript only)" apply to TypeScript code only; they do not apply to Rust code in `pnpm/` or `pnpr/`. Everything else applies repo-wide unless a nested `AGENTS.md` specializes it.
11+`pnpr/` is a pnpm-compatible npm registry server written in Rust —
12+roughly the role [verdaccio](https://verdaccio.org/) plays in the JS
13+ecosystem. It is a **sibling** of `pnpm/`, not part of it.
1214  
13−## Keep pnpm and pacquet in sync
15+The two Rust projects share the same Cargo workspace at the repo root so
16+that the registry can depend directly on `pacquet-*` crates (tarball
17+handling, integrity hashes, manifest parsing, network plumbing, etc.) and
18+the `Cargo.lock` stays unified.
1419  
15−The two stacks are parallel implementations of the same CLI, kept behaviorally identical — the same flags, defaults, error codes, file formats, and lockfile shape. They are now at near-complete feature parity and are developed together, so **any user-visible change has to land in both at the same time.** Neither stack is downstream of the other: pacquet is a source of truth in its own right, not a port that trails the TypeScript CLI.
20+## Relationship to pacquet
1621  
17−When you change one side, do the equivalent change on the other in the same PR if you can. If you can't (different expertise, scope too large, or pacquet hasn't ported the surrounding feature yet), open the PR with just your side — call out in the description what still needs porting, and someone else will push the matching commits to the same PR before it lands.
22+- **`pnpm/`** is a *port* of the pnpm CLI. Its cardinal rule is
23+ "match pnpm exactly" — see [`../pnpm/AGENTS.md`](../pnpm/AGENTS.md).
24+- **`pnpr/`** has no pnpm-CLI counterpart to mirror. It is a new
25+ server. Behavior here is designed, not ported.
1826  
19−"User-visible" means anything that affects the CLI surface or the on-disk contract: command-line flags and defaults, environment-variable handling, lockfile/manifest/state-file format, error codes and messages, log emissions parsed by `@pnpm/cli.default-reporter`, store layout, hook semantics. Pure internal refactors, perf wins, and TS-only test cleanups don't need mirroring.
27+That means the "match upstream pnpm" discipline that governs `pnpm/`
28+does **not** apply here. The registry can pick its own architecture,
29+flags, and config format. It must still be compatible with the npm
30+registry protocol that pnpm (and npm, yarn, etc.) clients speak.
2031  
21−**Any user-visible change to either stack must be replicated in the other.**
32+## Layout
2233  
23−The pacquet-side conventions for keeping the two stacks aligned are in [`pnpm/AGENTS.md`](./pnpm/AGENTS.md#the-cardinal-rule).
34+Mirrors `pnpm/`:
2435  
25−## Repository Structure
26− 
27−The pnpm codebase is a monorepo managed by pnpm itself. The root contains functional directories organized by domain:
28− 
29−### Core Directories
30− 
31−- `pnpm/`: The CLI entry point and main package.
32−- `pkg-manager/`: Core package management logic (installation, linking, etc.).
33−- `resolving/`: Dependency resolution logic (resolvers for npm, tarballs, git, etc.).
34−- `fetching/`: Package fetching logic.
35−- `store/`: Store management logic (content-addressable storage).
36−- `lockfile/`: Lockfile handling, parsing, and utilities.
37− 
38−### CLI & Configuration
39− 
40−- `cli/`: CLI command implementations and infrastructure.
41−- `config/`: Configuration management and parsing.
42−- `hooks/`: pnpm hooks (readPackage, etc.).
43−- `completion/`: Shell completion support.
44− 
45−### Other Functional Directories
46− 
47−- `network/`: Network-related utilities (proxy, fetch, auth).
48−- `workspace/`: Workspace-related utilities.
49−- `exec/`: Execution-related commands (run, exec, dlx).
50−- `env/`: Node.js environment management.
51−- `cache/`: Cache-related commands and utilities.
52−- `patching/`: Package patching functionality.
53−- `reviewing/`: License and dependency review tools.
54−- `releasing/`: Release and publishing utilities.
55− 
56−### Shared Utilities
57− 
58−- `packages/`: Shared utility packages (constants, error handling, logger, types, etc.).
59−- `fs/`: Filesystem utilities.
60−- `crypto/`: Cryptographic utilities.
61−- `text/`: Text processing utilities.
62− 
63−### Rust Projects
64− 
65−- `pnpm/`: The pnpm CLI ported to Rust. Self-contained sub-project with its own crates, tests, and tooling — see [`pnpm/AGENTS.md`](./pnpm/AGENTS.md).
66−- `pnpr/`: The pnpm-compatible npm registry server. Self-contained sub-project with its own crates, tests, and tooling — see [`pnpr/AGENTS.md`](./pnpr/AGENTS.md).
67− 
68−## Setup & Build (TypeScript only)
69− 
70−To set up the environment and build the project:
71− 
72−```bash
73−pnpm install
74−pnpm run compile
36+```text
37+pnpr/
38+ AGENTS.md
39+ crates/
40+ pnpr/ -> package "pnpr"
41+ Cargo.toml
42+ README.md
43+ src/
44+ lib.rs -> library API
45+ main.rs -> binary entry point (ships the `pnpr` binary)
46+ # future sibling crates land here, see "New registry-only crates" below
7547 ```
7648  
77−To compile a specific package:
49+The Rust workspace itself, `rust-toolchain.toml`, `justfile`, and
50+`Cargo.lock` live at the **repo root** — run `cargo` and `just` from there.
51+`pnpr/crates/*` is wired into the root workspace `members`.
7852  
79−```bash
80−pnpm --filter <package_name> run compile
81−```
53+## Code reuse
8254  
83−**Important:** The pnpm CLI e2e tests (in `pnpm/test/`) use the **bundled** `pnpm/dist/pnpm.mjs`, not the individual package `lib/` outputs. After changing any package, you must rebuild the bundle before running e2e tests:
55+**Prefer existing `pacquet-*` crates over writing new code.** Before
56+implementing anything non-trivial, check whether `pacquet-*` already
57+solves it. Candidates worth checking first: `pacquet-tarball`,
58+`pacquet-crypto-hash`, `pacquet-crypto-shasums-file`,
59+`pacquet-package-manifest`, `pacquet-network`, `pacquet-registry`,
60+`pacquet-fs`, `pacquet-diagnostics`. Add a `pacquet-*` crate the same
61+way pacquet crates do: declare it in the root `[workspace.dependencies]`
62+(already done for the pacquet crates) and use `{ workspace = true }`
63+in this crate's `Cargo.toml`.
8464  
85−```bash
86−pnpm --filter pnpm run compile
87−```
65+If a piece of code currently inside `pnpm/` turns out to be genuinely
66+shared between the two stacks and the `pacquet-` prefix becomes
67+misleading, propose renaming/relocating it in a dedicated PR — not as a
68+drive-by during feature work.
8869  
89−This runs `tsgo --build`, linting, and `pnpm run bundle` (which bundles all packages into `pnpm/dist/pnpm.mjs`). Without this step, e2e tests will use a stale bundle and your changes won't be tested.
70+### New registry-only crates
9071  
91−## Testing (TypeScript only)
72+When the registry needs its own crate (logic that isn't shared with the
73+pnpm port and doesn't fit in `pnpm/`), put it under
74+`pnpr/crates/<short-name>/` and name the package
75+`pnpr-<short-name>` in its `Cargo.toml`. The
76+`pnpr/crates/*` glob in the root workspace `members` picks it up
77+automatically; just add the new crate to `[workspace.dependencies]` at
78+the root with the `pnpr-` prefix so other crates can use
79+`{ workspace = true }`.
9280  
93−Never run all tests in the repository as it takes a lot of time.
81+Use the `pnpr-` prefix exclusively for registry-only crates.
82+Don't reach for `pacquet-` to name something new on the registry side.
9483  
95−Run tests for a specific project instead:
84+## Dependencies
9685  
97−```bash
98−# From the project directory
99−pnpm test
86+Same rule as pacquet: a dependency that is already declared in
87+`[workspace.dependencies]` may be used by any crate that needs it.
88+Adding a new third-party crate to the workspace requires an explicit
89+human request (see [`../pnpm/AGENTS.md`](../pnpm/AGENTS.md#things-not-to-do)).
10090  
101−# From the root, filtering by package name
102−pnpm --filter <package_name> test
103−```
91+## Style, tests, commits
10492  
105−Or better yet, run tests for a specific file:
93+Follow the pacquet code-style guide
94+([`../pnpm/CODE_STYLE_GUIDE.md`](../pnpm/CODE_STYLE_GUIDE.md)) and the
95+pacquet contributing guide ([`../pnpm/CONTRIBUTING.md`](../pnpm/CONTRIBUTING.md))
96+for Rust-level conventions — imports, naming, ownership, error handling,
97+test layout. They are written for pacquet but apply to any Rust code in
98+this workspace.
10699  
107−```bash
108−pnpm --filter <package_name> test <file_path>
109−```
100+### Comments
110101  
111−Or a specific test case in a specific file:
102+Follow the repo-wide comment baseline in [`../AGENTS.md`](../AGENTS.md#comments) and the Rust-specific additions in [`../pnpm/AGENTS.md`](../pnpm/AGENTS.md#comments).
112103  
113−```bash
114−pnpm --filter <package_name> test <file_path> -t <test_name_pattern>
115−```
104+Commit messages use Conventional Commits with `pnpr` as the scope
105+(`feat(pnpr): ...`, `fix(pnpr): ...`).
116106  
117−## Linting (TypeScript only)
107+Run the same checks pacquet does before declaring work done:
118108  
119−To run all linting checks:
120− 
121−```bash
122−pnpm run lint
109+```sh
110+just check # cargo check --locked --workspace --all-targets
111+just test # cargo nextest run
112+just lint # cargo clippy --workspace --all-targets -- --deny warnings
113+just fmt # cargo fmt + taplo format
123114 ```
124− 
125−## Never ignore test failures
126− 
127−Do not dismiss a failing test as a "pre-existing" failure that is unrelated to your changes. Every test failure must be investigated and fixed. If a test was already broken before your changes, fix it as part of your work — do not silently skip it or treat it as acceptable.
128− 
129−## AI Review Guidance
130− 
131−The repository's review framework lives in **[REVIEW_GUIDE.md](./REVIEW_GUIDE.md)** — how changes are accepted or rejected, the security-first / performance-second priorities, the security checklist and advisory regression themes, and the test/changeset/parity expectations. Apply it when reviewing pull requests. (TypeScript-specific code style and engineering conventions for the CLI are documented in the "Code Style" section of this file; pacquet and pnpr follow their own `AGENTS.md` and style guides.)
132− 
133−Security is the first review priority and performance the second. Surface only issues tied to the changed code, and explain the exploit path, impact, or hot path affected. See the guide's Security and Performance review sections for the full checklist.
134− 
135−## Code Reuse and Avoiding Duplication
136− 
137−**Before writing new code, always analyze the existing codebase for similar functionality.** This is a large monorepo with many shared utilities — duplication is a real risk.
138− 
139−- **Search before you write.** Before implementing any non-trivial logic, search the codebase for existing functions, utilities, or patterns that do the same or similar thing. Check `packages/`, `fs/`, `crypto/`, `text/`, and other shared directories first.
140−- **Extract shared code.** If you find that the logic you need already exists in another package but is not exported or reusable, refactor it into a shared package rather than duplicating it. If you are adding new code that is similar to code that already exists elsewhere in the repo, move the common parts into a shared package that both locations can use.
141−- **Prefer open source packages over custom implementations.** Do not reimplement functionality that is already available as a well-maintained open source package. Use established libraries for common tasks (e.g., path manipulation, string utilities, data structures, schema validation). Only write custom code when no suitable package exists or when the existing packages are too heavy or unmaintained.
142−- **Keep the dependency on the right level.** When adding a new open source dependency, add it to the most specific package that needs it, not to the root or to a shared package unless multiple packages depend on it.
143− 
144−## Commit Messages
145− 
146−Follow the [Conventional Commits](https://www.conventionalcommits.org/) specification.
147− 
148−- `feat`: a new feature
149−- `fix`: a bug fix
150−- `docs`: documentation only changes
151−- `style`: formatting, missing semi-colons, etc.
152−- `refactor`: code change that neither fixes a bug nor adds a feature
153−- `perf`: a code change that improves performance
154−- `test`: adding missing tests
155−- `chore`: changes to build process or auxiliary tools
156− 
157−### Install the git hooks before committing
158− 
159−The git hooks in `.husky/` (including the `commit-msg` check described below) only run once husky has wired them into git. A fresh clone does **not** have them active until installed. **Before making any commit, ensure the hooks are installed** by running one of:
160− 
161−```bash
162−pnpm install # runs the "prepare": "husky" script as part of install
163−# or, if dependencies are already installed, register the hooks on their own:
164−pnpm exec husky
165−```
166− 
167−You can confirm the hooks are active with `git config core.hooksPath` (it should point at husky's directory) and by checking that `.husky/_/` exists. Do not commit with hooks uninstalled — that silently skips every check, including the bare `#NNN` rejection below.
168− 
169−### Never use bare `#NNN` issue/PR references
170− 
171−**Do not write a bare `#NNN` (a `#` followed by digits) anywhere in a commit message.** A `commit-msg` hook (`.husky/reject-bare-issue-refs.mjs`) rejects them.
172− 
173−GitHub turns any `#NNN` into a link to issue/PR `NNN` of *this* repo, which is almost never what a bare reference means. This is a frequent AI mistake in two forms:
174− 
175−- Using `#1`, `#2`, `#3`, … to enumerate items in a list. GitHub instead links them to unrelated issues `#1`, `#2`, `#3` of this repo. **Fix:** don't use `#` for enumeration — write `item 1`, `(1)`, `1.`, or rephrase.
176−- Referring to issue `#NNN` of a *different* repository. GitHub instead links it to issue `NNN` of this repo. **Fix:** use qualified syntax `owner/repo#NNN` or an absolute URL `https://github.com/owner/repo/issues/NNN`.
177− 
178−For references to issues/PRs in **this** repo, also use the qualified form `pnpm/pnpm#NNN` or the absolute URL `https://github.com/pnpm/pnpm/issues/NNN`. Qualified syntax and absolute URLs are always unambiguous, so this rule is applied to every `#NNN` without exception.
179− 
180−**Address the root cause when the hook fires.** Rewrite the reference into the correct unambiguous form. Never bypass the check with `git commit --no-verify`, by editing or deleting the hook, or with any suppression file.
181− 
182−### Never use a bare `@mention`
183− 
184−**Do not write a bare `@name` (an `@` followed by a username-like token) anywhere in a commit message.** A `commit-msg` hook (`.husky/reject-bare-mentions.mjs`) rejects them.
185− 
186−GitHub turns any `@name` into a mention of that user/org/team, which is wrong either way it is meant:
187− 
188−- If it is code (a scoped package like `@pnpm/core`, a handle, a path), GitHub should not treat it as a mention.
189−- If it really is a person, every push, force-push, and rebase that carries the commit re-notifies them — noise nobody asked for.
190− 
191−**Fix:** wrap the reference in backticks so GitHub renders it as code and sends no notification — e.g. `` `@pnpm/core` `` or `` `@foo` `` — or remove it if it is not needed. Never bypass the check with `git commit --no-verify`, by editing or deleting the hook, or with any suppression file.
192− 
193−## Changesets
194− 
195−If your changes affect published packages, you MUST create a changeset file in the `.changeset` directory (`pnpm change` records one interactively; `pnpm change status` shows the pending release plan). The file describes the change and specifies the affected packages with their pending version bump types: patch, minor, or major. Write the description for pnpm users and keep it concise — it becomes a release note. Implementation rationale belongs in the commit message, not the changeset. The bare `pnpm version -r` consumes the pending changesets at release time; there is no separate `@changesets/cli` dependency.
196− 
197−**IMPORTANT: Always explicitly include `"pnpm"` in the changeset** with the appropriate version bump (patch, minor, or major). The pnpm CLI will only receive automatic patch bumps from its dependencies, so if your change warrants a minor or major version bump for the CLI, you must specify it explicitly. The changeset description will appear on the release notes page.
198− 
199−Example:
200− 
201−```
202−---
203−"@pnpm/installing.deps-installer": minor
204−"pnpm": minor
205−---
206− 
207−Added a new setting `blockExoticSubdeps` that prevents the resolution of exotic protocols in transitive dependencies [#10352](https://github.com/pnpm/pnpm/issues/10352).
208−```
209− 
210−**Versioning Guidelines for pnpm CLI:**
211−- **patch**: Bug fixes, internal refactors, and changes that don't require documentation updates
212−- **minor**: New features, settings, or commands that should be documented (anything users should know about)
213−- **major**: Breaking changes
214− 
215−### Changesets for the Rust products
216− 
217−The Rust products are released through the same native flow. Their npm wrapper packages are workspace packages with committed versions, so a user-visible change to a Rust product needs a changeset too, targeting:
218− 
219−- `pacquet` — the Rust pnpm CLI (published to npm as `pnpm` and `@pnpm/exe` under its `next-<major>` dist-tag; named `pacquet` in-repo so its name can't collide with the TypeScript CLI). `@pnpm/napi` is a `versioning.fixed` group with it and bumps with it automatically.
220−- `@pnpm/napi` — the Node.js addon bindings for the Rust engine.
221−- `@pnpm/pnpr` — the pnpr registry server (published as `@pnpm/pnpr` and its platform packages, plus the `ghcr.io/pnpm/pnpr` Docker image).
222− 
223−The Rust products release on `alpha` lanes (`versioning.lanes` in `pnpm-workspace.yaml`): each run of `pnpm version -r` that consumes an intent for one of them cuts an `X.Y.Z-alpha.N` prerelease, while the TypeScript CLI keeps releasing stable versions on the main lane. `pnpm lane main --filter …` graduates a product to a stable version.
224− 
225−Do not add `"pnpm"` to a Rust-only changeset: in changesets, `pnpm` always means the TypeScript CLI package. A changeset whose implementation is Rust-only and targets `pacquet` must omit `"pnpm"`. A parity change that lands in both stacks carries one changeset naming both the affected TypeScript packages (plus `"pnpm"`) and the Rust wrapper(s).
226− 
227−Use `pacquet` as the changeset package name, but use `pnpm` in its release-note prose and command examples (`pnpm add`, not `pacquet add`). The published Rust CLI's executable is `pnpm`; `pacquet` is only its in-repo package identifier.
228− 
229−## Comments
230− 
231−These conventions apply to the TypeScript pnpm CLI, pacquet, and pnpr. Product-specific `AGENTS.md` files may add language-specific rules, but they do not weaken this baseline.
232− 
233−Write code that explains itself. A reader should understand what a function does from its name, parameters, and types — not from prose above the call site.
234− 
235−Defaults:
236− 
237−- **Do not write a comment** that restates what the code already says. If renaming a variable, splitting a helper, or moving a check to a more obvious place would carry the information, do that instead.
238−- **Do not repeat documentation** at call sites that already lives on the callee. If the function has JSDoc, a Rust doc comment, or equivalent API documentation, the call site shouldn't re-explain what calling it does. Update the documentation once; let every call site benefit.
239−- **Put a shared *why* in one place.** When the same rationale underlies several related functions — peers that delegate to a common helper, or a type and its methods — document it once at that common home and reference it from the rest, instead of re-deriving it in each. This is the call-site rule applied sideways across peers, not just upward to a callee.
240−- **Documentation comments are for the item's contract** — preconditions, postconditions, edge cases, why the item exists. Not for re-narrating the body.
241−- **Do not record past implementation shape, refactor history, or "the previous code did X" framing.** That's what `git log` and `git blame` are for. Describe the current contract — what the code is and what it guarantees — not what it replaced. Phrasings like "used to", "previously", "the original X", or a parenthetical naming a removed type belong in the commit message, not in the source.
242− 
243−Write a comment only when:
244− 
245−- The reason for the code is non-obvious from reading it (a hidden invariant, a workaround for a known bug, a deliberate exception to the surrounding pattern).
246−- The right name doesn't fit — e.g., a temporary technical constraint that's worth flagging but doesn't justify a new symbol.
247− 
248−Before adding a comment, ask: "Could I rename, restructure, or extract instead?" If yes, do that. The bar for prose-in-code is high; the bar for prose-that-restates-code is "don't."
249− 
250−## Code Style (TypeScript only)
251− 
252−This repository uses [Standard Style](https://github.com/standard/standard) with a few modifications:
253−- **Trailing commas** are used.
254−- **Functions are preferred** over classes.
255−- **Functions are declared after they are used** (hoisting is relied upon).
256−- **Functions should have no more than two or three arguments.** If a function needs more parameters, use a single options object instead.
257−- **Import Order**:
258− 1. Standard libraries (e.g., `fs`, `path`).
259− 2. External dependencies (sorted alphabetically).
260− 3. Relative imports.
261− 
262−To ensure your code adheres to the style guide, run:
263− 
264−```bash
265−pnpm run lint
266−```
267− 
268−### Conventions
269− 
270−Recurring engineering conventions in this codebase — the rules reviewers most often enforce:
271− 
272−- **Errors.** Throw `PnpmError` (from `@pnpm/error`) for user-reachable errors — they are part of the UX and carry a stable code. Programmer-error, type-guard, and unreachable-branch errors stay plain `Error`. Never swallow errors; catch only the specific expected code (not "any error" when you meant `ENOENT`). Throw on impossible states rather than continuing. Error messages must carry context, e.g. the offending path.
273−- **Naming.** Functions are verbs; types and fields are specific, not generic. Reuse existing terminology rather than inventing synonyms. File names follow the existing convention; rename a concept everywhere it appears.
274−- **Reuse repo libraries.** Don't add a dependency, or hand-roll logic, for a job an existing repo utility or an already-present library does — search for it first. Deduplicate copy-pasted logic into a shared function or package.
275−- **String parsing.** Prefer plain string operations over a custom regular expression. When the input needs structured parsing with backtracking, use the existing parser-combinator pattern (`object/property-path`).
276−- **Dependency placement.** Shared infrastructure (the logger, etc.) is a peer dependency. (The narrowest-package rule is covered under "Code Reuse and Avoiding Duplication" above.)
277−- **Config and layering.** Configurable values flow through `@pnpm/config` and reach commands via options — don't hardcode them (CLI options are camelCased automatically). Command handlers return data and let the CLI print it, which keeps them unit-testable. Don't add a wrapper function that adds nothing.
278−- **Async and loops.** Prefer async fs and `async/await`; run independent work with `Promise.all`/`Promise.any` and `await` what must complete; hoist invariant work out of loops.
279− 
280−## Common Gotchas
281− 
282−### Error Type Checking in Jest (TypeScript only)
283− 
284−When checking if a caught error is an `Error` object, **do not use `instanceof Error`**. Jest runs tests in a VM context where `instanceof` checks can fail across realms.
285− 
286−Instead, use `util.types.isNativeError()`:
287− 
288−```typescript
289−import util from 'util'
290− 
291−try {
292− // ... some operation
293−} catch (err: unknown) {
294− // ❌ Wrong - may fail in Jest
295− if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {
296− return null
297− }
298−
299− // ✅ Correct - works across realms
300− if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
301− return null
302− }
303− throw err
304−}
305−```
306− 
307−## Working with GitHub PRs, Issues, and Comments
308− 
309−- **Open every PR with the repository template.** `gh pr create` does not apply `.github/pull_request_template.md` automatically, so read that file and pass its filled-in contents as the PR body (`--body`/`--body-file`). Keep every section (Summary, Squash Commit Body, Checklist), fill them in for this change, mark the checklist items, and remove only the lines the template says are inapplicable.
310−- **Keep PR titles and descriptions current.** When pushing new changes to a PR, review the title and description and update them if they no longer accurately reflect what the PR does.
311−- **Reply to and resolve review conversations.** Once a review comment has been addressed, reply to the thread with a description of the resolution including the commit hash that fixed it, then mark the conversation as resolved.
312−- **Sign all agent-authored content.** When posting a comment, creating an issue, or opening a PR, append a footer to the message indicating that it was written by an agent. The footer must include the name of the agent and the name of the model used. Example:
313− 
314− ```markdown
315− ---
316− Written by an agent (Claude Code, claude-opus-4-7).
317− ```
318− 
319−## Resolving Conflicts in GitHub PRs
320− 
321−Use `shell/resolve-pr-conflicts.sh` to resolve PR conflicts:
322− 
323−```bash
324−./shell/resolve-pr-conflicts.sh <PR_NUMBER>
325−```
326− 
327−The script force-fetches the base branch (avoiding stale refs), rebases, auto-resolves `pnpm-lock.yaml` conflicts via `pnpm install`, force-pushes, and verifies GitHub sees the PR as mergeable. For non-lockfile conflicts it will pause and list the files that need manual resolution.
328− 
329−## Key Configuration Files
330− 
331−- `pnpm-workspace.yaml`: Defines the workspace structure.
332−- `package.json` (root): Root scripts and devDependencies.
333−- `CONTRIBUTING.md`: Detailed contribution guidelines.
334115  
RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack