# AGENTS.md

This is a TypeScript library published to npm. The public API is the product, so compatibility comes before convenience.

## Project

Stack: TypeScript (strict), tsup for bundling, Vitest, Changesets, GitHub Actions, npm.

Layout:

- `src/index.ts` the only public entry point, everything exported here is API
- `src/internal/` implementation detail, never exported from index
- `src/types.ts` public types
- `test/` tests, imported through the package name so they exercise the real entry
- `dist/` build output, generated
- `docs/` usage documentation, `README.md` quick start
- `.changeset/` pending release notes

Supported runtimes: Node 20 and above, plus modern browsers. Output is ESM and CJS with type definitions.

## Commands

```bash
pnpm install
pnpm build                  # tsup, emits dist/ with esm, cjs, and .d.ts
pnpm dev                    # build in watch mode
pnpm test                   # vitest run
pnpm test --watch
pnpm test test/parse.test.ts
pnpm typecheck              # tsc --noEmit
pnpm lint
pnpm check:exports          # verify the export map resolves in esm and cjs
pnpm size                   # report bundle size against the budget
pnpm changeset              # record a release note for a user visible change
npm pack --dry-run          # inspect exactly what gets published
```

Run `pnpm build && pnpm test && pnpm typecheck` before returning work.

## Code style

- No side effects at import time. Importing the package must not touch globals, read the environment, or start a timer.
- Zero runtime dependencies unless there is no reasonable alternative. Prefer inlining twenty lines over adding a package.
- Everything exported from `src/index.ts` is public API. If it is not meant to be used, it does not belong there.
- Public functions have explicit return types. Do not rely on inference for anything that crosses the boundary.
- No default export. Named exports keep tree shaking and refactoring predictable.
- Errors are typed subclasses with a stable `name`, and messages say what was expected and what was received.
- No Node built ins in code paths that must run in a browser. Platform specific code goes behind a separate export condition.
- Every public function and type carries a TSDoc comment with at least one example.
- Options are passed as a single object, so parameters can be added without a breaking change.

## Boundaries

Do not touch without explicit instruction:

- `version` in `package.json` and `CHANGELOG.md`. Both are written by Changesets.
- `exports`, `main`, `module`, `types`, and `files` fields in `package.json`.
- `dist/` and any build output.
- `pnpm-lock.yaml`, `.github/workflows/`, `.npmrc`.

Needs human review and an explicit decision: removing or renaming anything exported, changing a function signature, changing default behavior, adding a runtime dependency, and raising the minimum supported Node version.

If a task appears to require a breaking change, describe the break and the migration path and stop.

## Testing

- `pnpm test` runs Vitest against the built package where possible, so the export map is exercised.
- Every exported function has tests for the documented behavior, the error cases, and the edge inputs named in its TSDoc.
- Type level tests live in `test/types.test-d.ts` using `expectTypeOf`. A type regression must fail the suite.
- Internal helpers are tested through the public API, not directly, so refactors do not break the suite.
- Bug reports become a failing test first, referencing the issue number in the test name.
- Do not lower a test to make it pass. If behavior must change, that is a release decision.

## Publishing

- Every user visible change requires a changeset: `pnpm changeset`, then pick patch, minor, or major.
- Semver is strict. A patch fixes behavior that was already documented, a minor adds API, a major removes or changes it.
- Deprecate before removing: mark with `@deprecated`, keep it working for at least one minor release, and document the replacement.
- Releases happen from `main` through CI. Do not run `npm publish` locally.
- Before a major, the migration guide in `docs/` must be updated in the same PR.
- `npm pack --dry-run` should contain `dist/`, `README.md`, and `LICENSE`, and nothing else.

## Git workflow

- Branch from `main`: `feat/short-description`, `fix/short-description`, `docs/short-description`.
- Conventional commits: `feat(parser): support trailing commas`.
- A PR with a user visible change and no changeset is incomplete.
- PR description states the API impact: none, additive, or breaking.
- Keep PRs focused. Refactors and behavior changes go in separate PRs so a regression can be bisected.
- Never push to `main`.
