Skip to content

Latest commit

 

History

History
436 lines (333 loc) · 16.3 KB

File metadata and controls

436 lines (333 loc) · 16.3 KB

AGENTS.md

Entry point for AI coding agents working in this repository. Read this file before any other. Full contributor guide: CONTRIBUTING.md.


Before writing any code

Important

Complete these steps before touching the codebase. They prevent duplicate work and give the PR a clear issue to close.

1. Search existing issues and PRshttps://github.com/openmaster-ai/clawmaster/issues

2. If no issue exists, create one using the right template:

Situation Template link
Something is broken Bug Report
New capability or improvement Feature Request
Picking up a roadmap item Contributor Sign-Up
Question about the codebase Discussions (not an issue)

3. Comment on the issue — state what you plan to do and how.

4. Create a branch from develop:

git checkout -b feat/short-description develop   # or fix/, docs/, test/, chore/

Branching & release (git-flow)

This project uses git-flow. Two long-lived branches exist at all times:

Branch Purpose
main Production-ready releases only. Every commit on main is a tagged release.
develop Integration branch. All feature/fix PRs target develop.

Branch types

Prefix Base → merge target Lifecycle
feat/ developdevelop Day-to-day feature work
fix/ developdevelop Non-urgent bug fixes
refactor/, docs/, test/, ci/, chore/ developdevelop Same as feat/fix
release/ developmain + develop Stabilisation before a release
hotfix/ mainmain + develop Critical production-only fixes

Day-to-day workflow

develop ──●──●──────●──────●── ...
           \      /
  feat/x    ●──●─┘   (PR → develop, squash-merge)
  1. Branch off develop.
  2. Open a PR targeting develop.
  3. Squash-merge when CI is green and review passes.

Cutting a release

develop ──●──●──┬──────────●── ...   (release branch merges back)
                 \        /
  release/0.2.0   ●──●──●           (bug-fixes only)
                          \
main ─────────────────────●── v0.2.0 (tag + merge)
  1. git checkout -b release/X.Y.Z develop
  2. Bump version in package.json (and src-tauri/tauri.conf.json for desktop).
  3. Only bug-fixes, docs, and metadata changes on this branch — no new features.
  4. Push and open two PRs (both from release/X.Y.Z):
    • PR to main — merge when CI is green
    • PR to develop — merge after main is done
  5. After the PR to main is merged, tag on main:
    git checkout main && git pull
    git tag -a vX.Y.Z -m "Release X.Y.Z"
    git push origin --follow-tags
  6. Delete the release branch after both PRs are merged.
  7. The vX.Y.Z tag triggers CI to build desktop bundles and create a GitHub release.

Never merge locally and push — always merge via GitHub PR so CI gates are enforced.

Hotfixes

main ──●────────────●── v0.2.1  (tag)
        \          /
  hotfix/0.2.1  ●─┘
                 \
develop ──●──────●── ...  (hotfix merges here too)
  1. git checkout -b hotfix/X.Y.Z main
  2. Fix the issue, bump patch version.
  3. Open a PR to main, wait for CI, merge on GitHub.
  4. Tag on main, then open a PR to develop and merge.

Rules

  • Never push directly to main or develop — always go through a GitHub PR so CI gates are enforced. No local git merge + git push to these branches.
  • Never merge main into develop — always merge release/hotfix branches back.
  • All merges to main must pass CI before merging — no bypassing branch protection.
  • Feature PRs that accidentally target main will be redirected to develop.
  • The develop branch should always be in a buildable, test-passing state.

Adding a feature

New features are capability modules in packages/web/src/modules/<name>/. The module system auto-discovers anything placed there — no registration step.

Minimal structure

modules/my-feature/
├── index.ts              ← exports ClawModule (required)
├── MyFeaturePage.tsx     ← main page component
└── __tests__/
    └── myFeature.test.ts ← required

index.ts shape

import { lazy } from 'react'
import type { ClawModule } from '@/app/modules/types'

export default {
  id: 'my-feature',
  name: 'myFeature.title',    // i18n key — never a raw string
  icon: 'lucide-icon-name',
  route: { path: '/my-feature', component: lazy(() => import('./MyFeaturePage')) },
  navOrder: 50,
} satisfies ClawModule

Data fetching

Use useAdapterCall — not raw useState/useEffect:

import { useAdapterCall } from '@/shared/hooks/useAdapterCall'
const { data, loading, error } = useAdapterCall(myAdapter.getSomething, { pollMs: 5000 })

Add the adapter in shared/adapters/my-feature.ts returning AdapterResult<T>:

import { ok, fail, wrapAsync } from '@/shared/adapters/types'
export const myAdapter = {
  getSomething: () => wrapAsync(async () => {
    const raw = await execCommandJson<MyType>('openclaw', ['my-feature', '--json'])
    return ok(raw)
  }),
}

i18n — required for every visible string

const { t } = useTranslation()
// ✓ correct
<h2>{t('myFeature.title')}</h2>
// ✗ never — hardcoded strings are rejected in review
<h2>My Feature</h2>

Add the key to all three runtime locale files before opening a PR:

  • packages/web/src/locales/main/zh.ts (Chinese — primary)
  • packages/web/src/locales/main/en.ts
  • packages/web/src/locales/main/ja.ts

Fixing a bug

  1. Write a failing unit test first that reproduces the bug.
  2. Fix the code until the test passes.
  3. Run npm test — all tests must be green.
  4. For UI bugs, verify the fix visually with dev-browser before opening a PR (see Screenshots in the PR body under Submitting a PR below).
npm test                         # full suite
npx vitest run src/path/to/test  # single file, from packages/web/
dev-browser --help               # full UI automation API reference

Submitting a PR

git push -u origin feat/my-feature
gh pr create --base develop --fill   # PRs target develop by default

Fill in ## What, ## Why, and ## How — the pr-description-check CI job rejects PRs with an empty ## What section.

Screenshots in the PR body. Any PR with user-visible changes — bug fixes, features, refactors that shift UI, anything under packages/web/src/modules/ — must include screenshots (or a short recording) under ## Screenshots as proof of the change. Drag-drop into the GitHub editor uploads to GitHub's CDN (preferred), or paste markdown from an image host. This is separate from the "no committed screenshot files" rule — embedding in the PR body is exactly where screenshots belong.

Checklist before marking ready for review:

  • npm test passes locally
  • npm run build passes (catches TypeScript errors)
  • New behavior has unit tests (happy path + at least one error path)
  • UI changes verified with dev-browser against npm run dev:web
  • UI changes include screenshots in the PR body's ## Screenshots section (drag-drop into the GitHub editor, or paste markdown from an image host)
  • All i18n keys added to packages/web/src/locales/main/{zh,en,ja}.ts
  • No console.log left in production paths
  • No screenshots, test logs, or generated files committed into the repo (dist/, coverage/) — embedding screenshots in the PR body is fine and encouraged
  • PR is a draft if not yet ready for review

Note

First-time contributors: a maintainer will add /ok-to-test after reviewing your diff before the full multi-platform Tauri build runs.


Architecture rules

Enforced by packages/web/src/shared/__tests__/architecture.boundary.test.ts. Violations cause CI to fail.

Rule What breaks it
shared/ must not import from modules/ or pages/ Adding import ... from '@/modules/...' in any shared file
modules/ must not import @tauri-apps/api directly Use tauriInvoke from shared/adapters/invoke.ts instead
pages/ must not import @tauri-apps/api directly Same — route through the shared adapter

Hard rules

Violating any of these will cause a PR to be rejected without review:

  • No new npm packages without an open issue and maintainer sign-off.
  • No new Rust crates without a maintainer with desktop experience signing off.
  • No Python, shell scripts, or non-Node.js runtimes as required dependencies.
  • No hardcoded display strings — every UI string goes through t().
  • No console.log in production code paths.
  • No generated files in commits: dist/, coverage/, src-tauri/target/, *.tsbuildinfo.
  • Branch prefix required: feat/, fix/, refactor/, docs/, test/, ci/, chore/, release/, hotfix/.

Technical reference

Common commands

npm install                  # install dependencies

npm run dev                  # web frontend only (port 16223)
npm run dev:web              # backend (port 16224) + frontend
npm run dev:backend          # Express backend only
npm run tauri:dev            # desktop app (Tauri)

npm run build                # production build + TypeScript check
npm run tauri:build          # desktop build (platform-specific)

npm test                     # run all Vitest tests

# Tauri build on Linux requires:
export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/share/pkgconfig
export PKG_CONFIG_PATH_x86_64_unknown_linux_gnu=$PKG_CONFIG_PATH

Two-mode runtime

The app detects its runtime in shared/adapters/platform.ts:

Desktop (Tauri)                     Web (Express)
──────────────────────────          ──────────────────────────
React → invoke() → Rust cmd         React → fetch('/api') → Express
        ↓                                   ↓
  src-tauri/lib.rs                  packages/backend/

All CLI calls go through execCommand() / execCommandJson<T>() in platform.ts. Never call invoke() or fetch('/api/exec') directly from a module or page.

Repo map

clawmaster/
├── packages/web/src/
│   ├── modules/            feature modules (new features go here)
│   │   ├── setup/          installation wizard + onboarding (special — see below)
│   │   ├── observe/        cost/token monitoring (Recharts)
│   │   ├── memory/         PowerMem management
│   │   ├── dashboard/      system overview + entry cards
│   │   ├── gateway/        runtime status and config
│   │   ├── channels/       channel setup and accounts
│   │   ├── models/         provider and model management
│   │   ├── skills/         ClawHub / skill install flows
│   │   ├── plugins/        plugin inventory
│   │   ├── mcp/            MCP install / import / manual config
│   │   ├── sessions/       runtime sessions
│   │   ├── settings/       profile, diagnostics, danger zone
│   │   ├── config/         raw openclaw.json editor
│   │   └── agents/         agent inventory
│   ├── shared/
│   │   ├── adapters/       per-tool adapters returning AdapterResult<T>
│   │   │   ├── platform.ts runtime detection + execCommand (single entry point)
│   │   │   ├── invoke.ts   tauriInvoke helper (only place @tauri-apps/api is allowed)
│   │   │   └── *.ts        one file per OpenClaw tool
│   │   ├── hooks/          useAdapterCall, useInstallTask
│   │   └── components/     ErrorBoundary, LoadingState, CapabilityGuard, PasswordField
│   ├── app/                routing, sidebar, startup, command registry
│   ├── pages/              legacy pages — do not add new code here
│   └── locales/main/       zh.ts · en.ts · ja.ts
├── packages/backend/       Express API server (standalone dev default port 16224)
├── src-tauri/              Tauri 2 desktop backend (Rust)
├── bin/clawmaster.mjs      CLI entry point
└── tests/ui/               YAML-based manual UI test plans

setup module

modules/setup/ is special — it exports:

  • SetupWizard component
  • getSetupAdapter() returning demoSetupAdapter | realSetupAdapter
  • CAPABILITIES and CapabilityId type (used by CapabilityGuard)

It covers 16 LLM providers and 6 channel types.

i18n

packages/web/src/locales/main/
├── zh.ts   Chinese (primary / fallback)
├── en.ts   English
└── ja.ts   Japanese

Language preference in localStorage key clawmaster-language. changeLanguage() exported from src/i18n/index.ts. Language switcher in the header and setup wizard.

Testing

Unit tests (Vitest)

  • Framework: Vitest + jsdom + @testing-library/react
  • Config: packages/web/vitest.config.ts
  • Location: co-located __tests__/ directories
  • Run single file: npx vitest run src/path/to/test.ts (from packages/web/)
  • Architecture boundary rules: shared/__tests__/architecture.boundary.test.ts

UI flow testing (dev-browser)

For verifying UI behaviour end-to-end, use dev-browser — a sandboxed Playwright runner built for AI agents.

# one-time setup
npm install -g dev-browser
dev-browser install          # installs Playwright + Chromium

Walk a UI flow against the running dev server:

# start the app first
npm run dev:web              # http://localhost:16223

# then drive it
dev-browser --headless <<'EOF'
const page = await browser.getPage("main");
await page.goto("http://localhost:16223", { waitUntil: "domcontentloaded" });
await page.screenshot({ path: "screenshot.png" });
console.log(await page.title());
EOF

Connect to an already-open Chrome instead of launching headless:

# launch Chrome with remote debugging enabled first
# macOS: open -a "Google Chrome" --args --remote-debugging-port=9222
dev-browser --connect <<'EOF'
const page = await browser.getPage("main");
// interact with the live app
EOF

The YAML-based UI test plans in tests/ui/ describe the flows to walk through. Run dev-browser --help for the full LLM-oriented API reference.

UI conventions

  • Styling: Tailwind CSS only — no custom CSS files
  • Icons: Lucide React only — no other icon libraries, no emoji in UI
  • Dark mode toggle (independent from color theme)
  • Color themes: Lobster Orange, Ocean Blue
  • Responsive: mobile hamburger menu

Rust / Tauri

  • Minimum Rust: 1.77.2
  • Commands registered in src-tauri/src/lib.rs via tauri::generate_handler![]
  • Config path: dirs::home_dir() / dirs::config_dir() from the dirs crate
  • Linux system deps: libglib2.0-dev libgtk-3-dev libwebkit2gtk-4.1-dev librsvg2-dev patchelf
  • Desktop targets: Linux (deb, rpm, AppImage), macOS (dmg, x64 + ARM64), Windows (msi + exe)

CI

Test Suite (test.yml) runs on every push/PR to develop, main, release/**, hotfix/**: TypeScript check → unit tests (546) → package install smoke (Linux/macOS/Windows) → backend integration → Tauri cargo check → E2E smoke.

Desktop Bundles (build.yml) only runs on release-track events:

  • Push to release/** or hotfix/** — validates bundles before tagging
  • Push of tag v* — produces release artifacts + publishes to npm + creates GitHub release
  • PR to main (i.e. release/hotfix PRs) — runs verify job for quick validation
  • Manual workflow_dispatch

Push to main/develop does not trigger desktop bundle builds — Test Suite covers regression checks, and tauri-check (fast cargo check) catches Rust compile errors. This keeps the slow multi-platform Tauri builds scoped to actual release work.