Coding style
This guide owns the coding conventions for WednesdayAI core. For the PR gate and contribution workflow, see Contributing. For test conventions, see Testing.Principles
Follow the Development Principles in Contributing. In short:- Reuse maintained packages already in the stack instead of building from scratch.
- Fix bug classes rather than instances. Use real parsers, not regex, for structured formats.
- Assert causes in tests.
- Keep scope to the goal.
- Block reviews only on in-scope correctness and security.
Language and structure
- Language: TypeScript ESM with
strict: true. No@ts-nocheck. Do not disableno-explicit-any; fix the root cause. - Style: functional and module pattern dominant. Use classes for channel adapters and stateful lifecycle managers.
- Dependency injection:
createDefaultDeps()insrc/cli/deps.tsreturns a plain dependency object. Commands receive it as a parameter. There is no IoC framework. - No prototype mutation: never use
applyPrototypeMixins,Object.definePropertyon.prototype, orClass.prototypeexports. Use explicit inheritance or composition. Get explicit approval first. - Comments: write them only for non-obvious logic such as hidden constraints, workarounds, and subtle invariants. Do not write docblocks that restate what the code does.
- File size: keep files under 500 lines of code.
pnpm check:locrunsscripts/check-ts-max-loc.ts --max 500. Split or refactor when it aids clarity or testability. - Formatting: run
pnpm checkbefore committing. Resolve formatting-only diffs without asking. - Naming: use WednesdayAI in user-facing docs and headings.
wednesdayaiis the primary binary name andopenclawis a permanent backward-compatible alias. Config keys and internal identifiers stayopenclaw. - Imports: use the
.jsextension for relative ESM imports,import type { X }for type-only imports, and import directly from the original module. Do not create files that only re-export another file.
Do not duplicate helpers
Before writing a formatter, utility, or helper, search for an existing implementation and import it. If a function already exists, do not create a copy in another file.
Never create local
formatAge, formatDuration, or formatElapsedTime functions.
Colors and terminal output
Two modules split the work.src/terminal/palette.ts holds the color tokens. src/terminal/theme.ts wraps chalk around those tokens and exposes the semantic helpers. Use theme for output and never hardcode ANSI escapes.
Logging
Create one subsystem logger at module top and use it instead ofconsole.log in production code:
Tool input schemas (TypeBox)
Tool input schemas use@sinclair/typebox. Several model providers reject schema constructs that TypeBox can produce, so keep schemas flat:
- No
Type.Union, and noanyOf,oneOf, orallOfin tool input schemas. - Use
stringEnumandoptionalStringEnum(aType.Unsafeenum, insrc/agents/schema/typebox.ts) for string enumerations. - Use
Type.Optional(...)instead of... | null. - Never use
formatas a raw property name. Some validators reject it.
Toolchain
Notes:
extensions/is excluded from Oxlint (ignorePatterns). Extensions manage their own lint.pnpm builddoes not type-check. Runpnpm tsgofor that.pnpm checkruns format check,tsgo,tsgo:web, Oxlint, several custom lint scripts, and the Swift host environment policy check.
Control UI decorators
The Control UI uses Lit with legacy decorators. The current Rollup parsing does not support theaccessor fields that standard decorators require. When you add reactive fields, keep the legacy style:
tsconfig.json sets experimentalDecorators: true and useDefineForClassFields: false. Do not change either unless you also update the UI build tooling to support standard decorators.