Use pnpm for all package management commands (not npm or yarn).
Exception: End-user install instructions should use npm (e.g. npx emulate, npm install emulate) since npm is universal.
emulate is a zsh built-in command (it sets shell emulation mode). Running bare emulate in zsh invokes the shell built-in, not the npm binary. Always use npx emulate in user-facing CLI examples, docs, skills, help output, and post-command messages. The only exception is when emulate appears as a subprocess argument to another tool (e.g. portless github.emulate emulate start), where the binary is resolved by the parent process rather than the shell.
Always check for the latest npm version when adding dependencies. Use pnpm add <package> (without version) to get the latest, or verify with npm view <package> version first.
Do not use emojis anywhere in this repository (code, comments, output, docs).
Never use -- as a dash in prose, comments, or user-facing output. Use an em dash (\u2014) when a dash is needed, but prefer rephrasing to avoid dashes entirely. The only exception is CLI flags (e.g. --port).
All emulator UIs (inspector pages, OAuth flows, checkout pages, inboxes, etc.) must use the shared design system in packages/@emulators/core/src/ui.ts. Never write inline HTML with custom <style> tags or standalone <!DOCTYPE html> templates in individual emulator packages.
Use the appropriate shared render function for each page type:
renderCardPagefor centered card layouts (OAuth sign-in, email detail, checkout)renderErrorPagefor error statesrenderSettingsPagefor sidebar + main content layouts (OAuth app settings, Slack inspector)renderInspectorPagefor tabbed data dashboards (AWS inspector)renderFormPostPagefor OAuthform_postauto-submit redirectsrenderUserButtonfor user selection buttons in OAuth flows
These functions provide the shared head() (Geist fonts, favicon, CSS), emuBar() header, and "Powered by emulate" footer automatically. Use the existing CSS classes (.inspector-table, .s-card, .org-row, .badge, .empty, etc.) rather than adding inline styles.
If a new page type cannot be built with the existing render functions and CSS classes, add the new styles and render function to core/src/ui.ts so every emulator can reuse them.
When a change affects how humans or agents use emulate (new/changed/removed commands, flags, behavior, routes, seed config, or SDK integration), update all of these:
README.mdskills/*/SKILL.md(agent skills for each service)apps/web/(docs site pages)- CLI
--helpoutput inpackages/emulate/src/index.ts
Releases are manual, single-PR affairs. The maintainer controls the changelog voice and format. All packages share a single version number (emulate + every @emulators/*).
To prepare a release:
- Create a branch (e.g.
prepare-v0.5.0) - Bump the version in
packages/emulate/package.json - Run
pnpm sync-versionsto update all@emulators/*packages - Write the changelog entry in
CHANGELOG.md, wrapped in<!-- release:start -->and<!-- release:end -->markers - Remove the
<!-- release:start -->and<!-- release:end -->markers from the previous release entry (only the latest release should have markers) - Open a PR and merge to
main
CI compares the version in packages/emulate/package.json to what's on npm. If it differs, it builds, publishes all packages with provenance, and creates the GitHub release automatically. The release body is extracted from the content between the markers.
Source code for dependencies is available in opensrc/ for deeper understanding of implementation details.
See opensrc/sources.json for the list of available packages and their versions.
Use this source code when you need to understand how a package works internally, not just its types/interface.
To fetch source code for a package or repository you need to understand, run:
npx opensrc <package> # npm package (e.g., npx opensrc zod)
npx opensrc pypi:<package> # Python package (e.g., npx opensrc pypi:requests)
npx opensrc crates:<package> # Rust crate (e.g., npx opensrc crates:serde)
npx opensrc <owner>/<repo> # GitHub repo (e.g., npx opensrc vercel/ai)