Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/clerk-monorepo-skill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
46 changes: 46 additions & 0 deletions .claude/skills/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Claude Code skills

This directory holds [Claude Code skills](https://code.claude.com/docs/en/skills) that ship with the
repo. Skills are how-to playbooks the agent loads on demand. They complement `AGENTS.md` (the
canonical rules, read automatically by most coding agents) with step-by-step recipes.

## How a skill is activated

There is nothing to install or enable. Anyone who checks out this repo and runs Claude Code in it
gets every skill in this directory automatically:

1. At session start, Claude only sees each skill's frontmatter `description`. This costs almost no
context.
2. When a request matches that description (for `clerk-monorepo`: setting up the repo, building or
testing a package, changesets, commit conventions, breaking-change questions), Claude loads the
full `SKILL.md` body on its own.
3. You can also invoke a skill explicitly by typing `/clerk-monorepo` in the prompt. `/skills` lists
everything available.
4. Files under `references/` are read only when the loaded skill points at them, so deep-dive
content stays out of context until it is needed.

Edits to a `SKILL.md` take effect immediately, including in already-running sessions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
ls -la .claude/skills || true
sed -n '1,80p' .claude/skills/README.md
nl -ba .claude/skills/README.md | sed -n '15,35p'

Repository: clerk/javascript

Length of output: 2967


🌐 Web query:

Claude Code SKILL.md edits take effect already-running session re-read invoked skill content stays in session

💡 Result:

Claude Code features live change detection for SKILL.md files, which allows edits to take effect in an already-running session without requiring a full restart [1][2][3]. However, once a skill is invoked, its content is injected into the session as a single message and remains there for the duration of that session [1][2][3]. Claude Code does not re-read or refresh the SKILL.md file on subsequent turns within the same session after it has already been invoked [1][2][3]. Consequently, if you edit a SKILL.md file after having already triggered the skill during your current session, Claude will continue to use the version of the instructions it loaded during the initial invocation [1][3]. To ensure Claude uses the updated instructions, you must either start a new session or be aware that the changes will only apply if the skill is triggered for the first time after the file has been edited [1][2][3]. For changes to plugin-based skill components—such as hooks,.mcp.json, agents, or output-styles—you must use the /reload-plugins command to apply updates [1][2][3].

Citations:


Clarify when SKILL.md edits apply during an active session.

.claude/skills/README.md line 22 claims edits apply immediately in already-running sessions, but Claude Code keeps the already-invoked skill instructions loaded for the session and doesn’t refresh the SKILL.md on subsequent turns; updates take effect after re-invoking the skill (or starting a new session).

🔧 Suggested wording
-Edits to a `SKILL.md` take effect immediately, including in already-running sessions.
+Edits to a `SKILL.md` take effect for new invocations; re-invoke the skill or start a new session to pick up changes.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Edits to a `SKILL.md` take effect immediately, including in already-running sessions.
Edits to a `SKILL.md` take effect for new invocations; re-invoke the skill or start a new session to pick up changes.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/README.md at line 22, The README's statement that edits to
SKILL.md "take effect immediately, including in already-running sessions" is
incorrect; update the sentence in README.md to clarify that the skill
instruction is loaded when a skill is invoked and edits to SKILL.md will not
affect an already-invoked skill until the skill is re-invoked or a new session
is started, e.g., replace the claim with wording that changes take effect after
re-invoking the skill or starting a new session so readers understand the
SKILL.md is not refreshed mid-session.


## Scope

Skills are Claude Code specific. Cursor does not read this directory; it uses `.cursor/rules/` and
`AGENTS.md`. When a repo rule changes, update `AGENTS.md` first, then mirror the change here and in
`.cursor/rules/` where relevant.

## Maintaining a skill

- `AGENTS.md` is the authority. If a skill disagrees with it, fix the skill.
- Keep `SKILL.md` lean; the whole body enters context when the skill triggers. Push detail into
`references/*.md`, which load on demand.
- Write the frontmatter `description` as the trigger: it is the only part the model sees up front,
so it should say _when_ to use the skill, not just what it contains.
- Skills contain factual claims about the repo (scripts, package lists, CI behavior). When you
rename a script, add a package, or change a workflow, grep this directory for stale mentions.
- `pnpm format` does not cover `.claude/`. Format skill files with
`pnpm prettier --write '.claude/**/*.md'`.

## Skills in this repo

| Skill | Use it for |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `clerk-monorepo` | Day-to-day work in the monorepo: setup, build/test loops, the package map, changesets, commits, PRs, breaking-change checks. |
167 changes: 167 additions & 0 deletions .claude/skills/clerk-monorepo/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
---
name: clerk-monorepo
description: >-
Work effectively in the clerk/javascript SDK monorepo. Use when setting up the repo,
building / testing / running a package, deciding which of the @clerk/* packages to
change, writing changesets, conventional commits, or PRs, or checking whether a change
is a breaking change to clerk-js or ui. Covers the pnpm + turbo dev loop, the package
map, and the repo's hard rules. AGENTS.md is the authority on the rules; this skill is
the how-to layer that points back to it.
---

# Working in the clerk/javascript monorepo

This is Clerk's JavaScript SDK monorepo: 23 packages (20 published `@clerk/*` plus the private
`@clerk/msw`, `@clerk/headless`, and `@clerk/swingset`) managed with pnpm
workspaces and Turborepo. Read this before building, testing, committing, or touching anything
under `packages/`.

`AGENTS.md` (repo root) is the canonical source of truth for the hard rules. This skill restates
those rules in actionable form and links back to it. If a rule here ever disagrees with `AGENTS.md`,
`AGENTS.md` wins, and the discrepancy should be fixed here.

## Fast setup (happy path)

The order matters more than the commands. Do them in sequence:

1. **Node `>=24.15.0`** (pinned in `.nvmrc`). `nvm use` if you have nvm. Wrong Node version is the
single most common cause of cryptic "cannot find module @clerk/..." build errors.
2. **`corepack enable`** before installing. The `preinstall` hook runs `only-allow pnpm`; npm/yarn
are hard-blocked. Corepack pins the right pnpm (`>=10.33.0`).
3. **`pnpm install` from the repo root** (never a subdirectory). It is a workspace; installing from
a package dir leaves cross-package links broken.
4. **`pnpm build`** before anything else. Packages depend on each other's built `dist/` + `.d.ts`.
Skipping this makes `dev`, tests, and the editor's types all report phantom errors.
5. **`pnpm dev`** to start watch mode.

Full sequence, the 11 footguns, and the internal integration-test / 1Password setup live in
[`references/setup-and-footguns.md`](references/setup-and-footguns.md).

## Package map: where does X live?

The ~10 packages people touch most. Full 23-package table, the dependency pyramid, and the complete
"change X, touch Y" routing are in [`references/package-map.md`](references/package-map.md).

| Package | You change it when... |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `@clerk/shared` | Utilities used everywhere (storage, events, React helpers). Most-depended-on; changes fan out to ~20 packages. Types live here too (`@clerk/shared/types`). |
| `@clerk/backend` | Server-side: JWT verification, the Backend API REST client, webhooks. Used by every framework adapter. |
| `@clerk/clerk-js` | ⚠️ The browser runtime loaded via script tag. **Backwards-compat sensitive** (see rules). |
| `@clerk/ui` | ⚠️ The React components powering the hosted sign-in / sign-up UI. **Backwards-compat sensitive**. |
| `@clerk/react` | Shared React hooks/context (`useAuth`, `useUser`, ...) consumed by the React-based adapters. |
| `@clerk/nextjs` | Next.js SDK: middleware, route handlers, server components. |
| `@clerk/express` | Express middleware and server helpers. |
| `@clerk/expo` | React Native / Expo SDK. |
| `@clerk/localizations` | UI translation strings (consumed by `ui`). |
| `@clerk/testing` | E2E helpers for consumers (Playwright / Cypress). |

> Heads-up: `packages/` may contain stale leftover dirs (`types`, `remix`, `themes`, `elements`, ...)
> with only build artifacts and no `package.json`. Those are removed packages, not active ones. The
> authoritative list is the git-tracked `packages/*/package.json` files.

## Dev-loop recipes

```bash
# Build one package (and its deps, via turbo ^build)
pnpm turbo build --filter=@clerk/nextjs

# Watch subsets instead of everything
pnpm dev:fe-libs # clerk-js + ui + shared
pnpm dev:js # clerk-js only
pnpm dev:sandbox # rspack sandbox for previewing UI components

# Run one package's unit tests (builds the package and its deps first)
pnpm turbo test --filter=@clerk/backend
# Faster, after a full build, for tight iteration:
pnpm --filter @clerk/backend test

# Run a single test file (vitest matches by filename substring). No `--` before the path:
# pnpm forwards a literal `--` into the script and vitest then ignores the filter.
pnpm --filter @clerk/shared test path/to/file.test.ts
# @clerk/backend runs a multi-runtime suite (run-s), so target one runtime for a single file:
pnpm --filter @clerk/backend test:node path/to/file.test.ts

# Quality gates (CI runs these)
pnpm lint
pnpm format # workspace packages plus root files, docs/, integration/, scripts/
pnpm prettier --write '.claude/**/*.md' # pnpm format does not cover .claude/; format skill files this way

# Changesets
pnpm changeset # for package-affecting changes
pnpm changeset:empty # for repo/tooling-only changes (see rules)
```

Test runner differs by package (`shared`, `clerk-js`, most adapters use vitest; `backend` runs a
multi-runtime suite), but the `pnpm --filter <name> test` invocation is uniform.

If the editor or a build reports stale types from `@clerk/shared`, rebuild the foundations:
`pnpm turbo build --filter=@clerk/shared`.

Integration-test variants (`pnpm test:integration:*`) and canary/snapshot releases are the long tail:
see [`references/setup-and-footguns.md`](references/setup-and-footguns.md) and `docs/CONTRIBUTING.md`.

## The hard rules

Each rule below restates `AGENTS.md`; the parenthetical is how it is enforced.

- **pnpm only, Node `>=24.15`, pnpm `>=10.33`.** (`preinstall` blocks npm/yarn; `engines` in
`package.json`.)
- **Every PR needs a changeset.** Use `pnpm changeset` for anything that affects a published package.
Use `pnpm changeset:empty` for repo/tooling-only changes; an empty changeset is **two `---`
delimiters with no body** (local `CLAUDE.local.md` convention). A changeset is a changelog entry
for users upgrading, not a summary of the diff. (CI fails PRs missing a changeset.)
- **Conventional commit `type(scope):`, scope is mandatory.** Enforced on the **PR title**
(`.github/workflows/pr-title-linter.yml`), not on individual commits. There is no local
`commit-msg` hook. Valid `scope` = any `packages/*` short name **and** its `clerk-`-stripped form
(so `clerk-js` accepts `clerk-js` or `js`), plus `repo`, `release`, `e2e`, `ci`, `*`. `docs` is a
valid **type**, not a scope. Source of truth: `commitlint.config.ts`.
- **`clerk-js` and `ui` must stay backwards-compatible across non-major releases.** A new `clerk-js`
runtime loads into apps still pinned to an _older_ framework SDK (`@clerk/nextjs`, etc.), so
removing or renaming anything an older SDK calls breaks those apps in production. (`break-check`
flags API-surface changes in `api-changes.yml`, but that check is informational; shipping such a
change means a `major`, gated by `major-version-check.yml`.)
- **Changes to the core `Clerk` class API (`packages/clerk-js/src/core/clerk.ts`) require a major
version** and `!allow-major` approval. (`.github/workflows/major-version-check.yml`.) APIs prefixed
`__internal_` or exported from an `/experimental` subpath are exempt from SemVer guarantees.

## PR / changeset / commit flow

1. Branch off `main`.
2. Make the change in the right package(s); add/update unit tests next to the code.
3. `pnpm changeset` (or `pnpm changeset:empty`).
4. Verify locally: `pnpm build`, `pnpm test` (or the filtered forms above), `pnpm lint`,
`pnpm format:check`.
5. Open the PR; the title must be a valid conventional commit (it becomes the squash commit). Fill in
the PR template.

Release _policy_ (when/how things ship, canary, snapshot, backports) is in `docs/PUBLISH.md`. This
skill stops at opening the PR.

## Breaking-change quick check

If you are editing `clerk-js` or `ui`, answer these. **Any "yes" means it is breaking**, needs a
major + `!allow-major`, and `break-check` will flag it:

1. Removing or renaming a public export, method, or property?
2. Changing a public function/method signature (new required arg, changed return type)?
3. Changing the `Clerk` class public surface in `core/clerk.ts`?
4. Renaming/removing something an older SDK version still calls at runtime?

If the symbol is `__internal_`/`__experimental_`-prefixed or under `/experimental`, it is exempt.
Full decision matrix:
[`references/breaking-changes.md`](references/breaking-changes.md).

## Deeper references

- `AGENTS.md`: the canonical hard rules (authority for this skill).
- `docs/CONTRIBUTING.md`: full setup, testing, JSDoc/Typedoc, changeset writing.
- `docs/PUBLISH.md`: release process (stable, canary, snapshot, backport, `!allow-major`).
- `docs/CICD.md`: CI/CD pipeline and automated releases.
- `docs/SECURITY.md`: vulnerability reporting (do **not** open public issues).
- `references/theming-architecture.md`: deep dive on the `@clerk/ui` appearance/theming system.
- Bundled: [`setup-and-footguns.md`](references/setup-and-footguns.md),
[`package-map.md`](references/package-map.md),
[`breaking-changes.md`](references/breaking-changes.md).

For analyzing or coordinating a **release PR** (the "Version packages" PR), use the dedicated
`analyze-javascript-release` and `coordinate-clerk-release` skills, not this one.
79 changes: 79 additions & 0 deletions .claude/skills/clerk-monorepo/references/breaking-changes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Breaking changes

Why `clerk-js` and `ui` carry a stricter contract than the rest of the monorepo, how to tell if a
change is breaking, and what CI does about it. `AGENTS.md` is the authority; this expands on it.

## Why `clerk-js` and `ui` are special

Most packages follow ordinary SemVer: consumers pin a version and upgrade deliberately. `clerk-js`
is different. Its **non-major releases are pushed to consuming apps without those apps updating any
dependency**. A browser loads the latest `clerk-js` runtime even when the app is still pinned to an
older framework SDK (`@clerk/nextjs`, `@clerk/react`, etc.). `AGENTS.md` puts `ui` under the same
contract: its compiled runtime reaches apps the same way, without a version bump. (`clerk-js` does
not declare `@clerk/ui` as a package dependency; `@clerk/ui` is a workspace dep of the framework
adapters, e.g. `react`/`astro`/`vue`/`chrome-extension`. The constraint is about the delivered
runtime, not the dependency graph.)

Consequence: a new `clerk-js`/`ui` runtime must keep working for **every SDK version still in the
wild**, not just the current monorepo state. Removing or renaming anything an older SDK calls at
runtime breaks those apps in production. This is the single most important constraint when editing
these two packages.

## The `Clerk` class API contract

`packages/clerk-js/src/core/clerk.ts` defines the public `Clerk` class. Its public surface (methods,
properties, constructor, static members like `Clerk.version`) is a contract depended on by internal
and external consumers, including older SDKs loading the latest runtime. **Changes to it require a
major version.** Treat it as frozen unless you are doing a deliberate major.

## Is my change breaking? Decision matrix

For a change in `clerk-js` or `ui`, any "yes" makes it breaking:

1. **Remove or rename** a public export, method, or property.
2. **Change a signature**: add a required parameter, change a parameter's type, or change a return
type of a public function/method.
3. **Change the `Clerk` class public surface** in `core/clerk.ts` (any of the above on it).
4. **Change runtime behavior an older SDK relies on**: rename an event, change a thrown error's
shape, alter the meaning of an existing option.

Not breaking (safe in a minor/patch):

- Adding a new optional parameter, method, property, or export.
- Internal refactors with no change to the public surface.
- Changes to anything prefixed `__internal_` or `__experimental_`.
- Changes to anything exported from an `/experimental` subpath (explicitly outside SemVer).

When unsure, assume breaking and check with the team. Cheaper than a production regression in apps
you cannot redeploy.

## Internal and experimental escape hatches

If you need to ship something that is not yet stable:

- Prefix methods/properties with `__internal_` (or `__experimental_` for experimental additions to
existing APIs) to signal "no SemVer guarantee."
- Or export from an `/experimental` subpath.

Both are documented in `docs/CONTRIBUTING.md` (the "Experimental and internal APIs" section) and are
exempt from the breaking-change rules above.

## What CI enforces

- **`break-check`** (`.github/workflows/api-changes.yml`) diffs the public API surface
(`.d.ts` declarations) and flags removals, renames, and signature changes. Treat it as a signal,
not a gate: the job runs `continue-on-error`, and an AI reviewer (`ai.applyDowngrades` in
`break-check.config.json`) can reclassify findings, so it is informational. Still investigate
anything it flags rather than assuming a false positive.
- **Major Version Check** (`.github/workflows/major-version-check.yml`) is the gate for a major: a
`major` changeset bump fails the check until a public org member comments `!allow-major`. (It is
not yet in `main`'s required-checks list, so treat a red check as blocking even when the merge
button is not.) The exact comment commands (`!allow-major`, `!snapshot`, `!preview`) and when to
use each are in `docs/PUBLISH.md`.

## If a breaking change is genuinely required

1. Confirm it truly cannot be done additively (new optional API, deprecate-then-remove later).
2. Discuss with the team; major releases are coordinated, not casual.
3. Write a changeset with a `major` bump and a clear migration path for consumers.
4. Expect the `!allow-major` approval step. See `docs/PUBLISH.md`.
Loading
Loading