atcute is a monorepository, a collection of lightweight and high-quality TypeScript libraries for AT Protocol (the protocol powering Bluesky.)
the packages are organized into categories under packages/:
clients/: API client implementationsservers/: XRPC server framework and runtime adaptersoauth/: OAuth implementationslexicons/: lexicon schema toolingdefinitions/: generated validation and type definitions for various AT Protocol servicesidentity/: DID document and handle resolutions, and did:plc validationutilities/: DASL codecs, data encoding and atproto primitivesmisc/: general-purpose utilitiesbluesky/: Bluesky-specific helpersinternal/: non-public development packages
development notes#
project management#
- tools like Node.js, Bun and pnpm are managed by mise
- Node.js can run TypeScript files directly (
node index.ts, strip types is unflagged) - check
pnpm view <package>before adding a new dependency
root-level tasks#
- format via
pnpm run fmt(oxfmt) - lint via
pnpm run lint(oxlint)
package-level tasks#
run these inside the package directory, e.g. cd packages/utilities/cbor; pnpm run ...
- build via
pnpm run build(tsgo, includes typechecking) - test via
pnpm run test(vitest)
code writing#
- new files should be in kebab-case
- use tabs for indentation, spaces allowed for diagrams in comments
- use single quotes for strings; use template literals for localization strings (user-facing strings, error messages)
- add trailing commas
- prefer arrow functions, but use regular methods in classes unless arrow functions are necessary
(e.g., when passing the method as a callback that needs
thisbinding) - use braces for control statements, even single-line bodies
- use bare blocks
{ }to group related code and limit variable scope - prefer
switchoverif/else ifchains when branching on a single discriminant value - avoid barrel exports (index files that re-export from other modules); import directly from source
- use
// #region <name>and// #endregionto denote regions when a file needs to contain a lot of code - a parameter should be optional only when callers genuinely split between passing a value and relying on the default; if every caller passes a value, make it required; if no caller would ever change it, it should not be a parameter at all
- avoid optional parameters that change behavioral modes or make the function do different things based on presence/absence; prefer a separate function with a clearer name instead
- avoid type assertions (
as Type,as const) unless TypeScript actually errors without them; when it does error, prefer finding a solution that satisfies the type system naturally before resorting to an assertion
commit workflow#
we use conventional commits with these rules:
- accepted types:
feat,fix,refactor,docs,choredocs: only applies to Markdown documents (README and similar)chore: only applies to build/tooling/dependency changes, and mass-autofixes from linters and formatters
- optional scope is the package name, e.g.
refactor(package-a): - omit the scope when the change does not involve any specific package, or when it touches most/all packages
- never list multiple packages in the scope (e.g.
refactor(package-a,package-b)is forbidden) - append
!after the type/scope to mark breaking changes, e.g.feat(package-a)!:orrefactor!:
scope selection when multiple packages are involved:
- if the change primarily involves
package-aoverpackage-b, pickpackage-a - if changes in
package-aandpackage-bhinge onpackage-c(even ifpackage-citself was not modified), pickpackage-c
granularity — each commit should represent one logical change:
- split distinct changes into separate commits rather than bundling them
- pair each changeset with the single commit it describes, so the changeset's git hash maps to the right change; do not write one changeset covering multiple commits
- pair each README update with the commit it documents, rather than batching doc updates across multiple changes
documentation#
- documentations include README, code comments, commit messages
- any writing should be in lowercase, except for proper nouns, acronyms and 'I'; this does not apply to public-facing interfaces like web UI
- only comment non-trivial code, focusing on why rather than what
- write comments and JSDoc in lowercase (except proper nouns, acronyms, and 'I')
- add JSDoc comments to new publicly exported functions, methods, classes, fields, and enums
- JSDoc should include proper annotations:
- use
@paramfor parameters (no dashes after param names) - use
@returnsfor return values - use
@throwsfor exceptions when applicable - keep descriptions concise but informative
- use
agentic coding#
.research/directory in the project root serves as a workspace for temporary experiments, analysis, and planning materials. create if not present (it's gitignored). this directory may contain cloned repositories or other reference materials that can help inform implementation decisions- this document is intentionally incomplete; discover everything else in the repo
- don't make assumptions or speculate about code, plans, or requirements without exploring first; pause and ask for clarification when you're still unsure after looking into it
- in plan mode, present the plan for review before exiting to allow for feedback or follow-up questions
- when debugging problems, isolate the root cause first before attempting fixes: add logging, reproduce the issue, narrow down the scope, and confirm the exact source of the problem
Claude Code-specific#
- Explore subagent may not be accurate; verify findings as needed
- never spawn subagents to read and return file contents; read files directly in the main context. subagents should perform searches or answer specific questions, not act as file I/O proxies
- don't use WebFetch to retrieve full page contents; it answers a question about a URL, not dumps
the raw content. use
curlif you need the complete unsummarized response
external repository research#
use @oomfware/cgr to ask questions about external repositories:
npx @oomfware/cgr ask [options] <repo>[#branch] <question>
options:
-m, --model <model> model to use: opus, sonnet, haiku (default: haiku)
-d, --deep clone full history (enables git log/blame/show)
-w, --with <repo> additional repository to include, supports #branch (repeatable)
useful repositories for development:
github.com/bluesky-social/atprotofor AT Protocol reference implementation, lexicons, XRPCgithub.com/bluesky-social/social-appfor Bluesky app patterns, API usage examplesgithub.com/bluesky-social/feed-generatorfor feed generator architecturegithub.com/bluesky-social/indigofor Go implementation, alternative design approachesgithub.com/bluesky-social/ozonefor moderation service patternsgithub.com/bluesky-social/proposalsfor AT Protocol proposals and specificationsgithub.com/bluesky-social/atproto-websitefor AT Protocol spec documentationgithub.com/DavidBuchanan314/atmstfor MST implementation in Python (@atcute/mst is derived from this)github.com/DavidBuchanan314/millipdsfor practical atmst usage patterns (Python)github.com/darobin/dasl.ingfor DASL specificationgithub.com/did-method-plc/did-method-plcfor DID PLC implementation reference
broad questions work for getting oriented; detailed questions get precise answers. include file/folder paths when you know them, and reference details from previous answers in follow-ups.
run npx @oomfware/cgr --help for more options.