23 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 - For narrowly scoped changes, prefer narrowly scoped tests that directly validate the touched behavior. If no meaningful scoped test exists, say so explicitly and use the next most direct validation available.
- Preferred landing bar for pushes to
main:pnpm checkandpnpm test, with a green result when feasible. - Scoped tests prove the change itself.
pnpm testremains the defaultmainlanding bar; scoped tests do not replace full-suite gates by default. - 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. - Default rule: do not commit or push with failing format, lint, type, build, or required test checks when those failures are caused by the change or plausibly related to the touched surface.
- For narrowly scoped changes, if unrelated failures already exist on latest
origin/main, state that clearly, report the scoped tests you ran, and ask before broadening scope into unrelated fixes or landing despite those failures. - Do not use scoped tests as permission to ignore plausibly related failures.
Coding Style & Naming Conventions
- Language: TypeScript (ESM). Prefer strict typing; avoid
any. - Formatting/linting via Oxlint and Oxfmt.
- 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.