Imported from xmazu/functhis (
AGENTS.md). Install upstream withnpx skills add xmazu/functhis. Copyright stays with the author.
Ultracite Code Standards
This project uses Ultracite, a zero-config preset that enforces strict code quality standards through automated formatting and linting.
Quick Reference
- Format code:
bun x ultracite fix - Check for issues:
bun x ultracite check - Diagnose setup:
bun x ultracite doctor
Oxlint + Oxfmt (the underlying engine) provides robust linting and formatting. Most issues are automatically fixable.
Git Commits
Follow Conventional Commits. Every commit message:
<type>(<optional scope>): <description>
- Use lowercase
typeand imperative description (what the commit does, not what you did) - Keep the description under 72 characters; put detail in the body
- Do not end the subject with a period
- Use a body when the why is not obvious from the subject
- Breaking changes:
BREAKING CHANGE:in the body, or!after the type/scope (feat(api)!: ...)
Types:
| Type | Use for |
|---|---|
feat |
New user-facing capability |
fix |
Bug fix |
docs |
Documentation only |
style |
Formatting; no behavior change |
refactor |
Behavior-preserving code change |
perf |
Performance improvement |
test |
Tests only |
build |
Build system or dependencies |
ci |
CI configuration |
chore |
Maintenance that does not fit the others |
Examples: feat(cli): add device login, fix(auth): reject special-use CIMD hosts, docs: describe terraform and wrangler split.
Core Principles
Write code that is accessible, performant, type-safe, and maintainable. Focus on clarity and explicit intent over brevity.
Type Safety & Explicitness
- Use explicit types for function parameters and return values when they enhance clarity
- Prefer
unknownoveranywhen the type is genuinely unknown - Use const assertions (
as const) for immutable values and literal types - Leverage TypeScript's type narrowing instead of type assertions
- Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Modern JavaScript/TypeScript
- Use arrow functions for callbacks and short functions
- Prefer
for...ofloops over.forEach()and indexedforloops - Use optional chaining (
?.) and nullish coalescing (??) for safer property access - Prefer template literals over string concatenation
- Use destructuring for object and array assignments
- Use
constby default,letonly when reassignment is needed, nevervar
Async & Promises
- Always
awaitpromises in async functions - don't forget to use the return value - Use
async/awaitsyntax instead of promise chains for better readability - Handle errors appropriately in async code with try-catch blocks
- Don't use async functions as Promise executors
React & JSX
- Use function components over class components
- Call hooks at the top level only, never conditionally
- Specify all dependencies in hook dependency arrays correctly
- Use the
keyprop for elements in iterables (prefer unique IDs over array indices) - Nest children between opening and closing tags instead of passing as props
- Don't define components inside other components
- Use semantic HTML and ARIA attributes for accessibility:
- Provide meaningful alt text for images
- Use proper heading hierarchy
- Add labels for form inputs
- Include keyboard event handlers alongside mouse events
- Use semantic elements (
<button>,<nav>, etc.) instead of divs with roles
Error Handling & Debugging
- Remove
console.log,debugger, andalertstatements from production code - Throw
Errorobjects with descriptive messages, not strings or other values - Use
try-catchblocks meaningfully - don't catch errors just to rethrow them - Prefer early returns over nested conditionals for error cases
Code Organization
- Keep functions focused and under reasonable cognitive complexity limits
- Extract complex conditions into well-named boolean variables
- Use early returns to reduce nesting
- Prefer simple conditionals over nested ternary operators
- Group related code together and separate concerns
Security
- Add
rel="noopener"when usingtarget="_blank"on links - Avoid
dangerouslySetInnerHTMLunless absolutely necessary - Don't use
eval()or assign directly todocument.cookie - Validate and sanitize user input
Performance
- Avoid spread syntax in accumulators within loops
- Use top-level regex literals instead of creating them in loops
- Prefer specific imports over namespace imports
- Avoid barrel files (index files that re-export everything)
- Use proper image components (e.g., Next.js
<Image>) over<img>tags
Framework-Specific Guidance
Next.js:
- Use Next.js
<Image>component for images - Use
next/heador App Router metadata API for head elements - Use Server Components for async data fetching instead of async Client Components
React 19+:
- Use ref as a prop instead of
React.forwardRef
Solid/Svelte/Vue/Qwik:
- Use
classandforattributes (notclassNameorhtmlFor)
Testing
- Before adding or changing integration tests, read docs/integration-tests.md and tests/integration/README.md
- Write assertions inside
it()ortest()blocks - Avoid done callbacks in async tests - use async/await instead
- Don't use
.onlyor.skipin committed code - Keep test suites reasonably flat - avoid excessive
describenesting
When Oxlint + Oxfmt Can't Help
Oxlint + Oxfmt's linter will catch most issues automatically. Focus your attention on:
- Business logic correctness - Oxlint + Oxfmt can't validate your algorithms
- Meaningful naming - Use descriptive names for functions, variables, and types
- Architecture decisions - Component structure, data flow, and API design
- Edge cases - Handle boundary conditions and error states
- User experience - Accessibility, performance, and usability considerations
- Documentation - Add comments for complex logic, but prefer self-documenting code
Design documents
apps/web/DESIGN.md and apps/console/DESIGN.md are visual language only: color, type, spacing, motion, surfaces, and general UX principles.
Never add product features, page sections, component inventories, implementation recipes, or copy decks to those files. Product behavior belongs in vision.md and architecture.md; UI structure belongs in code. When the look changes, update tokens and principles - not a catalog of widgets.
Do not restyle apps/console from the web design file, or apps/web from the console design file.
Monorepo placement
Put code in packages/ only when it is shared - imported from more than one app or package (e.g. web + console, or packages/auth + apps/web). If a module has a single consumer, keep it under that app (e.g. apps/web/src/server/…) until a second consumer exists, then extract.
| Location | Use for |
|---|---|
apps/<app>/src/… |
Routes, server handlers, UI, and helpers used only by that app |
packages/* |
Schema, auth, UI kit, CLI, and other cross-cutting libraries with real multi-consumer use |
packages/api is the shared oRPC surface: procedures and types that more than one app will call (web, console, MCP HTTP later). Do not add app-only procedures there. See architecture.md for the full rule.
Do not add to packages/ “for organization” or “might be reused later” without a second consumer today - that spreads coupling and makes ownership unclear.
Unused and barrel-only code
Remove dead code; do not grow public surfaces “just in case.”
- Run
bun run knip(also part ofbun run check) before finishing a change. Fix or delete what it reports: unused files, unused exports, unused dependencies. - Barrel files (
index.tsthat re-export symbols) must not be the only reason something exists. If a symbol is exported from a package entry or barrel but never imported outside that barrel chain, delete the symbol (and trim the barrel), not “leave it for the API.” - Prefer direct imports to the defining module over re-exporting through barrels when only one app needs the code (see also Avoid barrel files under Performance above).
Most formatting and common issues are automatically fixed by Oxlint + Oxfmt. Run bun x ultracite fix before committing to ensure compliance. Commit with a Conventional Commits subject as above.
