22 KiB
Repository Guidelines
- Repo: https://github.com/openclaw/openclaw
- In chat replies, file references must be repo-root relative only (example:
extensions/bluebubbles/src/channel.ts:80); never absolute paths or~/.... - Do not edit files covered by security-focused
CODEOWNERSrules unless a listed owner explicitly asked for the change or is already reviewing it with you. Treat those paths as restricted surfaces, not drive-by cleanup.
Project Structure & Module Organization
- Source code:
src/(CLI wiring insrc/cli, commands insrc/commands, web provider insrc/provider-web.ts, infra insrc/infra, media pipeline insrc/media). - Tests: colocated
*.test.ts. - Docs:
docs/(images, queue, Pi config). Built output lives indist/. - Plugins/extensions: live under
extensions/*(workspace packages). Keep plugin-only deps in the extensionpackage.json; do not add them to the rootpackage.jsonunless core uses them. - Plugins: install runs
npm install --omit=devin plugin dir; runtime deps must live independencies. Avoidworkspace:*independencies(npm install breaks); putopenclawindevDependenciesorpeerDependenciesinstead (runtime resolvesopenclaw/plugin-sdkvia jiti alias). - Import boundaries: extension production code should treat
openclaw/plugin-sdk/*plus localapi.ts/runtime-api.tsbarrels as the public surface. Do not import coresrc/**,src/plugin-sdk-internal/**, or another extension'ssrc/**directly. - Installers served from
https://openclaw.ai/*: live in the sibling repo../openclaw.ai(public/install.sh,public/install-cli.sh,public/install.ps1). - Messaging channels: always consider all built-in + extension channels when refactoring shared logic (routing, allowlists, pairing, command gating, onboarding, docs).
- Core channel docs:
docs/channels/ - Core channel code:
src/telegram,src/discord,src/slack,src/signal,src/imessage,src/web(WhatsApp web),src/channels,src/routing - Extensions (channel plugins):
extensions/*(e.g.extensions/msteams,extensions/matrix,extensions/zalo,extensions/zalouser,extensions/voice-call)
- Core channel docs:
- When adding channels/extensions/apps/docs, update
.github/labeler.ymland create matching GitHub labels (use existing channel/extension label colors).
Docs Linking (Mintlify)
- Docs are hosted on Mintlify (docs.openclaw.ai).
- Internal doc links in
docs/**/*.md: root-relative, no.md/.mdx(example:[Config](/configuration)). - When working with documentation, read the mintlify skill.
- For docs, UI copy, and picker lists, order services/providers alphabetically unless the section is explicitly describing runtime behavior (for example auto-detection or execution order).
- Section cross-references: use anchors on root-relative paths (example:
[Hooks](/configuration#hooks)). - Doc headings and anchors: avoid em dashes and apostrophes in headings because they break Mintlify anchor links.
- When Peter asks for links, reply with full
https://docs.openclaw.ai/...URLs (not root-relative). - When you touch docs, end the reply with the
https://docs.openclaw.ai/...URLs you referenced. - README (GitHub): keep absolute docs URLs (
https://docs.openclaw.ai/...) so links work on GitHub. - Docs content must be generic: no personal device names/hostnames/paths; use placeholders like
user@gateway-hostand “gateway host”.
Docs i18n (zh-CN)
docs/zh-CN/**is generated; do not edit unless the user explicitly asks.- Pipeline: update English docs → adjust glossary (
docs/.i18n/glossary.zh-CN.json) → runscripts/docs-i18n→ apply targeted fixes only if instructed. - Before rerunning
scripts/docs-i18n, add glossary entries for any new technical terms, page titles, or short nav labels that must stay in English or use a fixed translation (for exampleDoctororPolls). pnpm docs:check-i18n-glossaryenforces glossary coverage for changed English doc titles and short internal doc labels before translation reruns.- Translation memory:
docs/.i18n/zh-CN.tm.jsonl(generated). - See
docs/.i18n/README.md. - The pipeline can be slow/inefficient; if it’s dragging, ping @jospalmbier on Discord instead of hacking around it.
exe.dev VM ops (general)
- Access: stable path is
ssh exe.devthenssh vm-name(assume SSH key already set). - SSH flaky: use exe.dev web terminal or Shelley (web agent); keep a tmux session for long ops.
- Update:
sudo npm i -g openclaw@latest(global install needs root on/usr/lib/node_modules). - Config: use
openclaw config set ...; ensuregateway.mode=localis set. - Discord: store raw token only (no
DISCORD_BOT_TOKEN=prefix). - Restart: stop old gateway and run:
pkill -9 -f openclaw-gateway || true; nohup openclaw gateway run --bind loopback --port 18789 --force > /tmp/openclaw-gateway.log 2>&1 & - Verify:
openclaw channels status --probe,ss -ltnp | rg 18789,tail -n 120 /tmp/openclaw-gateway.log.
Build, Test, and Development Commands
- Runtime baseline: Node 22+ (keep Node + Bun paths working).
- Install deps:
pnpm install - If deps are missing (for example
node_modulesmissing,vitest not found, orcommand not found), run the repo’s package-manager install command (prefer lockfile/README-defined PM), then rerun the exact requested command once. Apply this to test/build/lint/typecheck/dev commands; if retry still fails, report the command and first actionable error. - Pre-commit hooks:
prek install(runs same checks as CI) - Also supported:
bun install(keeppnpm-lock.yaml+ Bun patching in sync when touching deps/patches). - Prefer Bun for TypeScript execution (scripts, dev, tests):
bun <file.ts>/bunx <tool>. - Run CLI in dev:
pnpm openclaw ...(bun) orpnpm dev. - Node remains supported for running built output (
dist/*) and production installs. - Mac packaging (dev):
scripts/package-mac-app.shdefaults to current arch. - Type-check/build:
pnpm build - TypeScript checks:
pnpm tsgo - Lint/format:
pnpm check - Format check:
pnpm format(oxfmt --check) - Format fix:
pnpm format:fix(oxfmt --write) - Tests:
pnpm test(vitest); coverage:pnpm test:coverage - Hard gate: before any commit,
pnpm checkMUST be run and MUST pass for the change being committed. - Hard gate: before any push to
main,pnpm checkMUST be run and MUST pass, andpnpm testMUST be run and MUST pass. - Hard gate: if the change can affect build output, packaging, lazy-loading/module boundaries, or published surfaces,
pnpm buildMUST be run and MUST pass before pushingmain. - Hard gate: do not commit or push with failing format, lint, type, build, or required test checks.
Coding Style & Naming Conventions
- Language: TypeScript (ESM). Prefer strict typing; avoid
any. - Formatting/linting via Oxlint and Oxfmt; run
pnpm checkbefore commits. - Never add
@ts-nocheckand do not disableno-explicit-any; fix root causes and update Oxlint/Oxfmt config only when required. - Dynamic import guardrail: do not mix
await import("x")and staticimport ... from "x"for the same module in production code paths. If you need lazy loading, create a dedicated*.runtime.tsboundary (that re-exports fromx) and dynamically import that boundary from lazy callers only. - Dynamic import verification: after refactors that touch lazy-loading/module boundaries, run
pnpm buildand check for[INEFFECTIVE_DYNAMIC_IMPORT]warnings before submitting. - Extension SDK self-import guardrail: inside an extension package, do not import that same extension via
openclaw/plugin-sdk/<extension>from production files. Route internal imports through a local barrel such as./api.tsor./runtime-api.ts, and keep theplugin-sdk/<extension>path as the external contract only. - Extension package boundary guardrail: inside
extensions/<id>/**, do not use relative imports/exports that resolve outside that sameextensions/<id>package root. If shared code belongs in the plugin SDK, importopenclaw/plugin-sdk/<subpath>instead of reaching intosrc/plugin-sdk/**or other repo paths via../. - Extension API surface rule:
openclaw/plugin-sdk/<subpath>is the only public cross-package contract for extension-facing SDK code. If an extension needs a new seam, add a public subpath first; do not reach intosrc/plugin-sdk/**by relative path. - Never share class behavior via prototype mutation (
applyPrototypeMixins,Object.definePropertyon.prototype, or exportingClass.prototypefor merges). Use explicit inheritance/composition (A extends B extends C) or helper composition so TypeScript can typecheck. - If this pattern is needed, stop and get explicit approval before shipping; default behavior is to split/refactor into an explicit class hierarchy and keep members strongly typed.
- In tests, prefer per-instance stubs over prototype mutation (
SomeClass.prototype.method = ...) unless a test explicitly documents why prototype-level patching is required. - Add brief code comments for tricky or non-obvious logic.
- Keep files concise; extract helpers instead of “V2” copies. Use existing patterns for CLI options and dependency injection via
createDefaultDeps. - Aim to keep files under ~700 LOC; guideline only (not a hard guardrail). Split/refactor when it improves clarity or testability.
- Naming: use OpenClaw for product/app/docs headings; use
openclawfor CLI command, package/binary, paths, and config keys. - Written English: use American spelling and grammar in code, comments, docs, and UI strings (e.g. "color" not "colour", "behavior" not "behaviour", "analyze" not "analyse").
Release / Advisory Workflows
- Use
$openclaw-release-maintainerat.agents/skills/openclaw-release-maintainer/SKILL.mdfor release naming, version coordination, release auth, and changelog-backed release-note workflows. - Use
$openclaw-ghsa-maintainerat.agents/skills/openclaw-ghsa-maintainer/SKILL.mdfor GHSA advisory inspection, patch/publish flow, private-fork checks, and GHSA API validation. - Release and publish remain explicit-approval actions even when using the skill.
Testing Guidelines
- Framework: Vitest with V8 coverage thresholds (70% lines/branches/functions/statements).
- Naming: match source names with
*.test.ts; e2e in*.e2e.test.ts. - Run
pnpm test(orpnpm test:coverage) before pushing when you touch logic. - Agents MUST NOT modify baseline, inventory, ignore, snapshot, or expected-failure files to silence failing checks without explicit approval in this chat.
- For targeted/local debugging, keep using the wrapper:
pnpm test -- <path-or-filter> [vitest args...](for examplepnpm test -- src/commands/onboard-search.test.ts -t "shows registered plugin providers"); do not default to rawpnpm vitest run ...because it bypasses wrapper config/profile/pool routing. - Do not set test workers above 16; tried already.
- If local Vitest runs cause memory pressure (common on non-Mac-Studio hosts), use
OPENCLAW_TEST_PROFILE=low OPENCLAW_TEST_SERIAL_GATEWAY=1 pnpm testfor land/gate runs. - Live tests (real keys):
CLAWDBOT_LIVE_TEST=1 pnpm test:live(OpenClaw-only) orLIVE=1 pnpm test:live(includes provider live tests). Docker:pnpm test:docker:live-models,pnpm test:docker:live-gateway. Onboarding Docker E2E:pnpm test:docker:onboard. - Full kit + what’s covered:
docs/help/testing.md. - Changelog: user-facing changes only; no internal/meta notes (version alignment, appcast reminders, release process).
- Changelog placement: in the active version block, append new entries to the end of the target section (
### Changesor### Fixes); do not insert new entries at the top of a section. - Changelog attribution: use at most one contributor mention per line; prefer
Thanks @authorand do not also addby @authoron the same entry. - Pure test additions/fixes generally do not need a changelog entry unless they alter user-facing behavior or the user asks for one.
- Mobile: before using a simulator, check for connected real devices (iOS + Android) and prefer them when available.
Commit & Pull Request Guidelines
-
Use
$openclaw-pr-maintainerat.agents/skills/openclaw-pr-maintainer/SKILL.mdfor maintainer PR triage, review, close, search, and landing workflows. -
This includes auto-close labels, bug-fix evidence gates, GitHub comment/search footguns, and maintainer PR decision flow.
-
For the repo's end-to-end maintainer PR workflow, use
$openclaw-pr-maintainerat.agents/skills/openclaw-pr-maintainer/SKILL.md. -
/landprlives in the global Codex prompts (~/.codex/prompts/landpr.md); when landing or merging any PR, always follow that/landprprocess. -
Create commits with
scripts/committer "<msg>" <file...>; avoid manualgit add/git commitso staging stays scoped. -
Follow concise, action-oriented commit messages (e.g.,
CLI: add verbose flag to send). -
Group related changes; avoid bundling unrelated refactors.
-
PR submission template (canonical):
.github/pull_request_template.md -
Issue submission templates (canonical):
.github/ISSUE_TEMPLATE/
Git Notes
- If
git branch -d/-D <branch>is policy-blocked, delete the local ref directly:git update-ref -d refs/heads/<branch>. - Agents MUST NOT create or push merge commits on
main. Ifmainhas advanced, rebase local commits onto the latestorigin/mainbefore pushing. - Bulk PR close/reopen safety: if a close action would affect more than 5 PRs, first ask for explicit user confirmation with the exact PR count and target scope/query.
Security & Configuration Tips
- Web provider stores creds at
~/.openclaw/credentials/; rerunopenclaw loginif logged out. - Pi sessions live under
~/.openclaw/sessions/by default; the base directory is not configurable. - Environment variables: see
~/.profile. - Never commit or publish real phone numbers, videos, or live configuration values. Use obviously fake placeholders in docs, tests, and examples.
- Release flow: use the private maintainer release docs for the actual runbook,
docs/reference/RELEASING.mdfor the public release policy, and$openclaw-release-maintainerfor the maintainership workflow.
Local Runtime / Platform Notes
- Vocabulary: "makeup" = "mac app".
- Rebrand/migration issues or legacy config/service warnings: run
openclaw doctor(seedocs/gateway/doctor.md). - Use
$openclaw-parallels-smokeat.agents/skills/openclaw-parallels-smoke/SKILL.mdfor Parallels smoke, rerun, upgrade, debug, and result-interpretation workflows across macOS, Windows, and Linux guests. - For the macOS Discord roundtrip deep dive, use the narrower
.agents/skills/parallels-discord-roundtrip/SKILL.mdcompanion skill. - Never edit
node_modules(global/Homebrew/npm/git installs too). Updates overwrite. Skill notes go intools.mdorAGENTS.md. - If you need local-only
.agentsignores, use.git/info/excludeinstead of repo.gitignore. - When adding a new
AGENTS.mdanywhere in the repo, also add aCLAUDE.mdsymlink pointing to it (example:ln -s AGENTS.md CLAUDE.md). - Signal: "update fly" =>
fly ssh console -a flawd-bot -C "bash -lc 'cd /data/clawd/openclaw && git pull --rebase origin main'"thenfly machines restart e825232f34d058 -a flawd-bot. - CLI progress: use
src/cli/progress.ts(osc-progress+@clack/promptsspinner); don’t hand-roll spinners/bars. - Status output: keep tables + ANSI-safe wrapping (
src/terminal/table.ts);status --all= read-only/pasteable,status --deep= probes. - Gateway currently runs only as the menubar app; there is no separate LaunchAgent/helper label installed. Restart via the OpenClaw Mac app or
scripts/restart-mac.sh; to verify/kill uselaunchctl print gui/$UID | grep openclawrather than assuming a fixed label. When debugging on macOS, start/stop the gateway via the app, not ad-hoc tmux sessions; kill any temporary tunnels before handoff. - macOS logs: use
./scripts/clawlog.shto query unified logs for the OpenClaw subsystem; it supports follow/tail/category filters and expects passwordless sudo for/usr/bin/log. - If shared guardrails are available locally, review them; otherwise follow this repo's guidance.
- SwiftUI state management (iOS/macOS): prefer the
Observationframework (@Observable,@Bindable) overObservableObject/@StateObject; don’t introduce newObservableObjectunless required for compatibility, and migrate existing usages when touching related code. - Connection providers: when adding a new connection, update every UI surface and docs (macOS app, web UI, mobile if applicable, onboarding/overview docs) and add matching status + configuration forms so provider lists and settings stay in sync.
- Version locations:
package.json(CLI),apps/android/app/build.gradle.kts(versionName/versionCode),apps/ios/Sources/Info.plist+apps/ios/Tests/Info.plist(CFBundleShortVersionString/CFBundleVersion),apps/macos/Sources/OpenClaw/Resources/Info.plist(CFBundleShortVersionString/CFBundleVersion),docs/install/updating.md(pinned npm version), and Peekaboo Xcode projects/Info.plists (MARKETING_VERSION/CURRENT_PROJECT_VERSION). - "Bump version everywhere" means all version locations above except
appcast.xml(only touch appcast when cutting a new macOS Sparkle release). - Restart apps: “restart iOS/Android apps” means rebuild (recompile/install) and relaunch, not just kill/launch.
- Device checks: before testing, verify connected real devices (iOS/Android) before reaching for simulators/emulators.
- iOS Team ID lookup:
security find-identity -p codesigning -v→ use Apple Development (…) TEAMID. Fallback:defaults read com.apple.dt.Xcode IDEProvisioningTeamIdentifiers. - A2UI bundle hash:
src/canvas-host/a2ui/.bundle.hashis auto-generated; ignore unexpected changes, and only regenerate viapnpm canvas:a2ui:bundle(orscripts/bundle-a2ui.sh) when needed. Commit the hash as a separate commit. - Release signing/notary credentials are managed outside the repo; maintainers keep that setup in the private maintainer release docs.
- Lobster palette: use the shared CLI palette in
src/terminal/palette.ts(no hardcoded colors); apply palette to onboarding/config prompts and other TTY UI output as needed. - When asked to open a “session” file, open the Pi session logs under
~/.openclaw/agents/<agentId>/sessions/*.jsonl(use theagent=<id>value in the Runtime line of the system prompt; newest unless a specific ID is given), not the defaultsessions.json. If logs are needed from another machine, SSH via Tailscale and read the same path there. - Do not rebuild the macOS app over SSH; rebuilds must be run directly on the Mac.
- Voice wake forwarding tips:
- Command template should stay
openclaw-mac agent --message "${text}" --thinking low;VoiceWakeForwarderalready shell-escapes${text}. Don’t add extra quotes. - launchd PATH is minimal; ensure the app’s launch agent PATH includes standard system paths plus your pnpm bin (typically
$HOME/Library/pnpm) sopnpm/openclawbinaries resolve when invoked viaopenclaw-mac.
- Command template should stay
Collaboration / Safety Notes
- When working on a GitHub Issue or PR, print the full URL at the end of the task.
- When answering questions, respond with high-confidence answers only: verify in code; do not guess.
- Never update the Carbon dependency.
- Any dependency with
pnpm.patchedDependenciesmust use an exact version (no^/~). - Patching dependencies (pnpm patches, overrides, or vendored changes) requires explicit approval; do not do this by default.
- Multi-agent safety: do not create/apply/drop
git stashentries unless explicitly requested (this includesgit pull --rebase --autostash). Assume other agents may be working; keep unrelated WIP untouched and avoid cross-cutting state changes. - Multi-agent safety: when the user says "push", you may
git pull --rebaseto integrate latest changes (never discard other agents' work). When the user says "commit", scope to your changes only. When the user says "commit all", commit everything in grouped chunks. - Multi-agent safety: do not create/remove/modify
git worktreecheckouts (or edit.worktrees/*) unless explicitly requested. - Multi-agent safety: do not switch branches / check out a different branch unless explicitly requested.
- Multi-agent safety: running multiple agents is OK as long as each agent has its own session.
- Multi-agent safety: when you see unrecognized files, keep going; focus on your changes and commit only those.
- Lint/format churn:
- If staged+unstaged diffs are formatting-only, auto-resolve without asking.
- If commit/push already requested, auto-stage and include formatting-only follow-ups in the same commit (or a tiny follow-up commit if needed), no extra confirmation.
- Only ask when changes are semantic (logic/data/behavior).
- Multi-agent safety: focus reports on your edits; avoid guard-rail disclaimers unless truly blocked; when multiple agents touch the same file, continue if safe; end with a brief “other files present” note only if relevant.
- Bug investigations: read source code of relevant npm dependencies and all related local code before concluding; aim for high-confidence root cause.
- Code style: add brief comments for tricky logic; keep files under ~500 LOC when feasible (split/refactor as needed).
- Tool schema guardrails (google-antigravity): avoid
Type.Unionin tool input schemas; noanyOf/oneOf/allOf. UsestringEnum/optionalStringEnum(Type.Unsafe enum) for string lists, andType.Optional(...)instead of... | null. Keep top-level tool schema astype: "object"withproperties. - Tool schema guardrails: avoid raw
formatproperty names in tool schemas; some validators treatformatas a reserved keyword and reject the schema. - Never send streaming/partial replies to external messaging surfaces (WhatsApp, Telegram); only final replies should be delivered there. Streaming/tool events may still go to internal UIs/control channel.
- For manual
openclaw message sendmessages that include!, use the heredoc pattern noted below to avoid the Bash tool’s escaping. - Release guardrails: do not change version numbers without operator’s explicit consent; always ask permission before running any npm publish/release step.
- Beta release guardrail: when using a beta Git tag (for example
vYYYY.M.D-beta.N), publish npm with a matching beta version suffix (for exampleYYYY.M.D-beta.N) rather than a plain version on--tag beta; otherwise the plain version name gets consumed/blocked.