This file is for agentic coding tools. It's a map β read linked docs for depth.
Nexu is a desktop-first OpenClaw platform. Users create AI bots, connect them to Slack, and the local controller generates OpenClaw config for the embedded runtime.
- Monorepo: pnpm workspaces
apps/controllerβ Single-user local control plane for Nexu config, OpenClaw sync, and runtime orchestrationapps/desktopβ Electron desktop runtime shell and sidecar orchestratorapps/webβ React + Ant Design + Viteopenclaw-runtimeβ Repo-local packaged OpenClaw runtime for local dev and desktop packaging; replaces globalopenclawCLIpackages/sharedβ Shared Zod schemaspackages/dev-utilsβ TS-first reusable utilities for local script tooling
Nexu is a desktop-first OpenClaw product. Users create AI bots via a dashboard and connect them to Slack. The system dynamically generates OpenClaw configuration and hot-loads it into the local runtime managed by the controller.
All commands use pnpm. Target a single app with pnpm --filter <package>.
pnpm install # Install
pnpm --filter @nexu/shared build # Build shared dist required by cold-start dev flows
pnpm dev start # Start the lightweight local stack: openclaw -> controller -> web -> desktop
pnpm dev start <service> # Start one local-dev service: desktop|openclaw|controller|web
pnpm dev restart # Restart the lightweight local stack
pnpm dev stop # Stop the lightweight local stack in reverse order
pnpm dev stop <service> # Stop one local-dev service
pnpm dev restart <service> # Restart one local-dev service
pnpm dev status <service> # Show status for one local-dev service
pnpm dev logs <service> # Show active-session log tail (max 200 lines) for one local-dev service
pnpm dev:controller # Legacy controller-only direct dev entrypoint
pnpm dist:mac # Build signed macOS desktop distributables
pnpm dist:mac:arm64 # Build signed Apple Silicon macOS desktop distributables
pnpm dist:mac:x64 # Build signed Intel macOS desktop distributables
pnpm dist:mac:unsigned # Build unsigned macOS desktop distributables
pnpm dist:mac:unsigned:arm64 # Build unsigned Apple Silicon macOS desktop distributables
pnpm dist:mac:unsigned:x64 # Build unsigned Intel macOS desktop distributables
pnpm probe:slack prepare # Launch Chrome Canary with the dedicated Slack probe profile
pnpm probe:slack run # Run the local Slack reply smoke probe against an authenticated DM
pnpm --filter @nexu/web dev # Web only
pnpm build # Build all
pnpm check:esm-imports # Scan built dist for extensionless relative ESM specifiers
pnpm typecheck # Typecheck all
pnpm lint # Biome lint
pnpm format # Biome format
pnpm test # Vitest
pnpm generate-types # OpenAPI spec β frontend SDKAfter API route/schema changes: pnpm generate-types then pnpm typecheck.
This repo is desktop-first. Prefer the controller-first path and remove or ignore legacy API/gateway/container-era assets when encountered.
mainis the integration branch and should stay releasable.- Do feature work on short-lived branches named with a clear prefix such as
feat/...,fix/..., orchore/.... - Prefer merging the latest
maininto long-running feature branches instead of rewriting shared history once a PR is under review. - After a PR merges, sync local
main, then delete the merged feature branch locally and remotely when it is no longer needed.
- No co-author trailer. Never append
Co-Authored-By:lines to commit messages. - Conventional commit prefix. Use
chore:for changes that are invisible to end users (CI/CD, issue bots, tooling, config). These are excluded from release notes. Usefeat:/fix:/docs:etc. for user-visible changes. - PR format. When creating a pull request, always follow
.github/pull_request_template.mdβ fill in What / Why / How / Affected areas / Checklist sections.
- Minimal cold-start setup on a fresh machine is:
pnpm install->pnpm --filter @nexu/shared build-> copyscripts/dev/.env.exampletoscripts/dev/.envonly if you need dev-only overrides. - Default daily flow is:
pnpm dev start->pnpm dev status <service>/pnpm dev logs <service>as needed ->pnpm dev stop. - Use
pnpm dev restartfor a clean full-stack recycle; usepnpm dev restart <service>only when you are intentionally touching one service. - Explicit single-service control remains available through
pnpm dev start <desktop|openclaw|controller|web>,pnpm dev stop <service>,pnpm dev restart <service>,pnpm dev status <service>, andpnpm dev logs <service>. pnpm devintentionally does not supportall; the full local stack order remainsopenclaw->controller->web->desktop.pnpm dev logs <service>is session-scoped, prints a fixed header, and tails at most the last 200 lines from the active service session.scripts/dev/.env.exampleis the source-of-truth template for dev-only overrides. Copy it toscripts/dev/.envonly when you need to override ports, URLs, state paths, or the shared OpenClaw gateway token for local development.- Keep the detailed startup optimization rules, cache invalidation behavior, and troubleshooting notes in
specs/guides/desktop-runtime-guide.md; keep only the core workflow expectations here. - The repo also includes a local Slack reply smoke probe at
scripts/probe/slack-reply-probe.mjs(pnpm probe:slack prepare/pnpm probe:slack run) for verifying the end-to-end Slack DM reply path after local runtime or OpenClaw changes. - The Slack smoke probe is not zero-setup: install Chrome Canary first, then manually log into Slack in the opened Canary window before running
pnpm probe:slack run. - The active desktop runtime path is controller-first: desktop launches
controller + web + openclawand no longer starts localapi,gateway, orpglitesidecars. - Desktop local runtime should not depend on PostgreSQL. In dev mode, all state (config, OpenClaw state, logs) lives under
.tmp/desktop/nexu-home/, fully isolated from the packaged app. Launchd plists go to.tmp/launchd/, runtime-ports.json also lives there. - In packaged mode, data is split across two directories (see table below). Launchd plists go to
~/Library/LaunchAgents/. - Local desktop runtime state is repo-scoped under
.tmp/desktop/in development.
| Directory | Purpose | Survives uninstall |
|---|---|---|
~/.nexu/ (NEXU_HOME) |
User config (config.json, cloud-profiles.json), compiled snapshots, skill ledger, skillhub cache, logs, openclaw-sidecar, nexu.db |
Yes |
~/Library/Application Support/@nexu/desktop/ (Electron userData) |
OpenClaw runtime state: runtime/openclaw/state/agents/ (conversations), runtime/openclaw/state/extensions/ (channel state), runtime/openclaw/state/skills/, runtime/openclaw/state/openclaw.json, plus Electron internal data (Cache, IndexedDB, etc.) |
No (cleaned by uninstall tools) |
The split is intentional: NEXU_HOME holds lightweight user preferences that should persist across reinstalls; Electron userData holds heavy runtime state tied to the app lifecycle. OPENCLAW_STATE_DIR is explicitly set by the desktop launcher to point to the userData path β do not rely on the controller's default fallback.
tmuxis no longer required for the desktop local-dev workflow; process state is tracked by the platform-aware launcher entrypoints.- To fully reset local desktop + controller state, stop the stack, remove
.tmp/desktop/, then remove~/.nexu/and~/Library/Application Support/@nexu/desktop/. - Desktop already exposes an agent-friendly runtime observability surface; prefer subscribing/querying before adding temporary UI or ad hoc debug logging.
- For deeper desktop runtime inspection, use the existing event/query path (
onRuntimeEvent(...),runtime:query-events,queryRuntimeEvents(...)) instead of rebuilding one-off diagnostics. - Use
actionId,reasonCode, andcursor/nextCursoras the primary correlation and incremental-fetch primitives for desktop runtime debugging. - Desktop runtime guide:
specs/guides/desktop-runtime-guide.md. - The controller sidecar is packaged by
apps/desktop/scripts/prepare-controller-sidecar.mjswhich deep-copies all controllerdependenciesand their transitive deps into.dist-runtime/controller/node_modules/. Keep controller deps minimal to avoid bloating the desktop distributable. - SkillHub (catalog, install, uninstall) runs in the controller via HTTP β not in the Electron main process via IPC. The web app always uses HTTP SDK for skill operations.
- Desktop auto-update is channel-specific. Packaged builds should embed
NEXU_DESKTOP_UPDATE_CHANNEL(stable/beta/nightly) so the updater checks the matching feed, and update diagnostics should always log the effective feed URL plus remoteversion/releaseDatewhen available.
- Debugging first principle: binary isolate, don't guess. For UI/runtime regressions, start with overall bisection and add tiny reversible
quick return/quick failprobes at key boundaries. Prefer changes that create obvious UI/log differences, narrow the fault domain quickly, and can be reverted immediately after verification. Do not start by rewriting route guards, state flows, or core logic based on intuition. - Never use
any. Useunknownwith narrowing orz.infer<typeof schema>. - No foreign keys in Drizzle schema β application-level joins only.
- Credentials (bot tokens, signing secrets) must never appear in logs or errors.
- Frontend must use generated SDK (
apps/web/lib/api/), never rawfetch. - All API routes must use
createRoute()+app.openapi()from@hono/zod-openapi. Never use plainapp.get()/app.post()etc β those bypass OpenAPI spec generation and the SDK won't have corresponding functions. - All request bodies, path params, query params, and responses must have Zod schemas. Shared schemas go in
packages/shared/src/schemas/, route-local param schemas (e.g.z.object({ id: z.string() })) can stay in the route file. - After adding or modifying API routes: run
pnpm generate-typesto regenerateopenapi.json->sdk.gen.ts->types.gen.ts, then update frontend call sites to use the new SDK functions. - Config generator output must match
specs/references/openclaw-config-schema.md. - Do not add dependencies without explicit approval.
- Do not modify OpenClaw source code.
- Never commit code changes until explicitly told to do so.
- Desktop packaged app: never use
npx,npm,pnpm, or any shell command that relies on the user's PATH. The packaged Electron app has no shell profile β resolve bin paths programmatically viarequire.resolve()and execute withprocess.execPath. The app must be fully self-contained. - Controller sidecar packaging: every dependency in
apps/controller/package.jsonis recursively deep-copied into the desktop distributable viaprepare-controller-sidecarβcopyRuntimeDependencyClosure. Never add heavy transitive-dependency packages (e.g.npm,yarn) to the controller. If the controller needs to shell out to a CLI tool, use PATH-basedexecFile("npm", ...)instead of bundling it as a dependency. Each MB added to controller deps adds ~1 MB to the final DMG/ZIP. - Native Node.js addons (e.g.
better-sqlite3) must live in the controller, NOT in the desktop Electron main process. Electron's built-in Node.js has a different ABI version (NODE_MODULE_VERSION) from system Node.js, requiringelectron-rebuildto recompile native modules. The controller runs as a regular Node.js process (ELECTRON_RUN_AS_NODE=1), so native addons work without recompilation.
- Request-level tracing must be created uniformly by middleware as the root trace.
- Logic with monitoring value must be split into named functions and annotated with
@Trace/@Span. - Do not introduce function-wrapper transitional APIs such as
runTrace/runSpan. - Iterate incrementally: add Trace/Span within established code patterns first, then refine based on metrics.
- Logger usage source of truth should follow the active package you are editing; prefer established nearby logger patterns in controller and desktop code.
pnpm typecheckβ after any TypeScript changespnpm lintβ after any code changespnpm generate-typesβ after API route/schema changespnpm testβ after logic changes
See ARCHITECTURE.md for the full bird's-eye view. Key points:
- Monorepo:
apps/controller(Hono),apps/web(React),apps/desktop(Electron),packages/shared(Zod schemas),nexu-skills/(skill repo) - Type safety: Zod -> OpenAPI -> generated frontend SDK. Never duplicate types.
- Config generator:
apps/controller/src/lib/openclaw-config-compiler.tsbuilds OpenClaw config from local controller state - Local runtime flow:
apps/controllerowns Nexu config/state, writes OpenClaw config/skills/templates, and managesopenclaw-runtimedirectly; desktop wraps that controller-first stack with Electron + web sidecars - Key data flows: local config compilation, desktop runtime boot, channel sync, file-based skill catalog
- Biome: 2-space indent, double quotes, semicolons always
- Files:
kebab-case/ Types:PascalCase/ Variables:camelCase - Zod schemas:
camelCase+Schemasuffix - DB tables:
snake_casein Drizzle - Public IDs: cuid2 (
@paralleldrive/cuid2), never exposepk - Errors: throw
HTTPExceptionwith status + contextual message - Logging: structured (pino or console JSON), never log credentials
| Topic | Location |
|---|---|
| Architecture & data flows | ARCHITECTURE.md |
| System design | specs/designs/openclaw-multi-tenant.md |
| OpenClaw internals | specs/designs/openclaw-architecture-internals.md |
| Engineering principles | specs/design-docs/core-beliefs.md |
| Config schema & pitfalls | specs/references/openclaw-config-schema.md |
| API coding patterns | specs/references/api-patterns.md |
| Workspace templates | specs/guides/workspace-templates.md |
| Local Slack testing | specs/references/local-slack-testing.md |
| Local Slack smoke probe | scripts/probe/README.md, scripts/probe/slack-reply-probe.mjs |
| Local dev CLI guidance | scripts/dev/AGENTS.md |
| Frontend conventions | specs/FRONTEND.md |
| Desktop runtime guide | specs/guides/desktop-runtime-guide.md |
| Security posture | specs/SECURITY.md |
| Reliability | specs/RELIABILITY.md |
| Product model | specs/PRODUCT_SENSE.md |
| Quality signals | specs/QUALITY_SCORE.md |
| Product specs | specs/product-specs/ |
| Execution plans | specs/exec-plans/ |
| Documentation sync | skills/localdev/sync-specs/SKILL.md |
| Nano Banana (image gen) | skills/nexubot/nano-banana/SKILL.md |
| Skill repo & catalog | nexu-skills/, apps/controller/src/services/skillhub/ |
| File-based skills design | specs/plans/2026-03-15-skill-repo-design.md |
| Feishu channel setup | apps/web/src/components/channel-setup/feishu-setup-view.tsx |
After significant code changes, verify documentation is current.
git diff --name-only $(git merge-base HEAD origin/main)...HEAD| Changed area | Affected docs |
|---|---|
apps/web/src/pages/ or routing |
specs/FRONTEND.md |
apps/controller/src/routes/ |
specs/references/api-patterns.md, specs/product-specs/*.md |
apps/controller/src/runtime/ |
ARCHITECTURE.md, specs/RELIABILITY.md |
apps/web/src/components/channel-setup/ |
specs/FRONTEND.md |
nexu-skills/ |
ARCHITECTURE.md (monorepo layout) |
packages/shared/src/schemas/ |
ARCHITECTURE.md (type safety) |
package.json scripts |
AGENTS.md Commands section |
| New/moved doc files | AGENTS.md Where to look |
AGENTS.mdWhere to look table β all paths validspecs/DESIGN.md<->specs/design-specs/+specs/designs/(indexed)specs/product-specs/index.md<-> actual spec filesspecs/FRONTEND.mdPages <->apps/web/src/app.tsxroutes
- Regenerate
specs/generated/db-schema.mdfully from schema source - Preserve original language (English/Chinese)
- Do not auto-commit; present changes for review
Full reference: skills/localdev/sync-specs/SKILL.md
Nexu work must be synced into the team knowledge repo at:
agent-digital-cowork/clone/
When producing artifacts in this repo, sync them to the cross-project repo using this mapping:
| Artifact type | Target in agent-digital-cowork/clone/ |
|---|---|
| Design plans / architecture proposals | design/ |
| Debug summaries / incident analysis | debug/ |
| Ideas / product notes | ideas/ |
| Stable facts / decisions / runbooks | knowledge/ |
| Open blockers / follow-ups | blockers/ |
Project memory directory:
/Users/alche/.claude/projects/-Users-alche-Documents-digit-sutando-nexu/memory/
Keep these memory notes up to date:
- Cross-project sync rules memory (source of truth for sync expectations)
- Skills hot-reload findings memory (
skills-hotreload.md) - DB/dev environment quick-reference memory
For OpenClaw skills behavior and troubleshooting, maintain and consult:
skills-hotreload.mdin the Nexu memory directory above.
This note should track:
- End-to-end pipeline status (
Controller store -> compiler -> runtime writers -> OpenClaw) - Why
openclaw-managedskills may be missing from session snapshots - Watcher/snapshot refresh caveats and validation steps
- Controller env path:
apps/controller/.env - Fresh local-dev cold start:
pnpm install->pnpm --filter @nexu/shared build-> optionalcopy scripts/dev/.env.example scripts/dev/.env(Windows) orcp scripts/dev/.env.example scripts/dev/.env(POSIX) ->pnpm dev start - Daily local-dev flow:
pnpm dev start->pnpm dev logs <service>/pnpm dev status <service>when needed ->pnpm dev restartfor a clean recycle ->pnpm dev stop - OpenClaw managed skills dir (expected default):
~/.openclaw/skills/ - Slack smoke probe setup: install Chrome Canary, set
PROBE_SLACK_URL, runpnpm probe:slack prepare, then manually log into Slack in Canary beforepnpm probe:slack run openclaw-runtimeis installed implicitly bypnpm install; local development should normally not use a globalopenclawCLI- Full-stack startup order is
openclaw->controller->web->desktop; shutdown order is the reverse - Prefer
./openclaw-wrapperover globalopenclawin local development; it executesopenclaw-runtime/node_modules/openclaw/openclaw.mjs - When OpenClaw is started manually, set
RUNTIME_MANAGE_OPENCLAW_PROCESS=falsefor@nexu/controllerto avoid launching a second OpenClaw process - If behavior differs, verify effective
OPENCLAW_STATE_DIR/OPENCLAW_CONFIG_PATHused by the running controller process.