Guidelines for working with code in this repository. These are linter-like rules and build/test instructions — not a code summary.
- TypeScript Backend — TypeScript backend packages (
packages/) - Web UI — React web application
- Components, Stores, Contexts, Hooks, Utils, ServerState, Lib, Config
- Testing — Web testing guide (Jest, React Testing Library, Playwright)
- Electron — Desktop app
- Mobile — React Native mobile app
- Agent System — Agent architecture, tools, skills, workflow nodes
- Scripts — Build and release scripts
- Workflow Runner — Standalone workflow runner
- Node.js 22.x (required — see
.nvmrc). Electron 35 embeds Node 22; native modules must match the ABI. - Use
nvm useto activate the correct version. - If you see
NODE_MODULE_VERSIONerrors, runnpm rebuild.
make install # Install all dependencies (web, electron, mobile)
make build # Build all packages
make typecheck # Type check all packages
make lint # Lint all packages
make lint-fix # Auto-fix linting issues
make test # Run all tests
make check # Run all checks (typecheck, lint, test)npm run build:packages # Build all in dependency order
npm run test:packages # Test all packages
npm run test --workspace=packages/<name> # Test single package
npm run test:watch --workspace=packages/<name> # Watch mode for single packagecd web
npm install # Install dependencies
npm start # Start dev server
npm run build # Production build
npm run typecheck # TypeScript check
npm run lint # ESLint
npm run lint:fix # Auto-fix lint issues
npm test # Run tests
npm run test:watch # Watch mode
npm run test:coverage # With coverage
npm run test:e2e # Run e2e tests (requires backend)cd electron
npm install # Install dependencies
npm start # Start electron
npm run build # Production build (tsc + vite + electron-builder)
npm run typecheck # TypeScript check
npm run lint # ESLint
npm run lint:fix # Auto-fix lint issues
npm test # Run testsmake dev # Backend (tsx --watch) + web Vite server
make dev-server # Backend dev server only (tsx --watch, port 7777)
make electron # Build web and start Electron app
make electron-dev # Electron against Vite server (requires conda env)After any code change, run:
make typecheck # Type check all packages
make lint # Lint all packages
make test # Run all testsAll three must pass before the task is complete.
Before submitting a PR, review for:
- Existing tests still pass and cover the changes
- No new TypeScript errors or lint warnings
- No unintended side effects in related code
- Edge cases and error handling are covered
- Performance implications considered
- Decorator packages load from
dist/:base-nodes,node-sdk,fal-nodes,replicate-nodes,elevenlabs-nodesuse decorators. After changing these, runnpm run build:packagesbefore runningmake dev. - Package build order matters: Always use
npm run build:packages(builds in dependency order). Don't build individual packages with unbuilt dependencies. - Mobile typecheck needs protocol: Run
cd packages/protocol && npm run buildbeforemake typecheck-mobile. - WebSocket uses MsgPack, not JSON: Use existing serialization helpers. Don't serialize WebSocket messages as JSON.
- Don't create WebSocket instances: Use
GlobalWebSocketManagersingleton in the frontend. - ES Modules everywhere: All packages use
"type": "module". Compiled imports need.jsextensions. - Never import from
dist/: Use@nodetool/<package>workspace references in source code.
- Use TypeScript for all new code. Never use
any. - Use
constby default,letwhen reassignment is needed. Never usevar. - Use strict equality (
===/!==). Exception:== nullfor null/undefined checks. - Always use curly braces for control statements.
- Use
Array.isArray()to check for arrays, nottypeof. - Throw
Errorobjects, not strings. - Always add comments for intentionally empty catch blocks.
- Use functional components only. No class components.
- Always define a TypeScript interface for component props.
- Never mutate state directly. Use immutable patterns.
- Don't use inline functions in JSX when passed to memoized child components.
- Test behavior, not implementation details.
| Hook | Use When | Do Not Use When |
|---|---|---|
useEffect |
Side effects (network, subscriptions, timers, DOM) | Deriving data from props/state |
useMemo |
Expensive computation, referential stability | Cheap computation |
useCallback |
Passing to memoized children, dependency of effect/memo | Function used only locally |
React.memo |
Pure component, stable props, renders often, expensive | Props change every render |
Never add these "just in case." If performance is fine, do nothing.
- Always prefix with
use. - Use descriptive names:
useWorkflowActionsnotuseActions. - Include all dependencies in
useEffect,useCallback,useMemoarrays. - Provide TypeScript types for all return values.
- Keep stores focused on a single domain.
- Use selectors to prevent unnecessary re-renders:
useStore(state => state.value). - Use shallow equality for object selections.
- Define actions within the store alongside state.
- Use
persistmiddleware for settings stored in localStorage.
- Use MUI components over custom HTML when available.
- Use
sxprop for one-off styles,styled()for reusable styles. - Use theme values for spacing, colors, and typography — not hardcoded values.
- Prefer composition over deep prop drilling.
- Use hierarchical query keys:
['workflows', workflowId]. - Set appropriate
staleTimebased on data volatility. - Use
enabledoption for conditional queries. - Use optimistic updates for mutations where appropriate.
- Always invalidate related queries after successful mutations.
- Components: PascalCase (
MyComponent.tsx) - Hooks: camelCase with
useprefix (useMyHook.ts) - Stores: PascalCase file, camelCase
useprefix for hook (useMyStore) - Utilities: camelCase (
formatDate.ts) - Constants: UPPER_SNAKE_CASE (
MAX_NODES) - Types/Interfaces: PascalCase (
NodeData) - Tests: Same as source +
.test.ts(x), placed in__tests__/directories
- React and core libraries
- Third-party libraries (MUI, TanStack Query, etc.)
- Internal stores and contexts
- Internal components
- Internal utilities and types
- Styles
- Use React Testing Library queries (
getByRole,getByLabelText). - Use
userEventfor interactions, notfireEvent. - Use
waitForfor async assertions. - Mock external dependencies and API calls.
- Test user-facing behavior, not implementation details.
- Keep tests independent and isolated.
E2E tests require the TypeScript backend and Node.js frontend. For comprehensive E2E testing documentation, see web/TESTING.md.
# Build the backend packages first (one time)
npm run build:packages
# Install and run
cd web
npm install
npx playwright install chromium
npm run test:e2e # Automatically starts servers
# Manual setup for debugging
# Terminal 1: PORT=7777 HOST=127.0.0.1 node packages/websocket/dist/server.js
# Terminal 2: cd web && npm start
# Terminal 3: cd web && npx playwright testcd electron
npm install
npx playwright install chromium
npm run vite:build && npx tsc # Build first
npm run test:e2e # Run E2E testsSee electron/src/AGENTS.md for Electron-specific testing.
- Use
DOMPurify.sanitize()for user input rendered as HTML. - Never use
dangerouslySetInnerHTMLwith unsanitized input. - Use
contextBridgefor Electron IPC — never exposenodeIntegration. - Validate all IPC inputs.
- Node.js LTS, TypeScript 5.4+, ES Modules
- Vitest for testing
- Key packages:
@nodetool/websocket(server),@nodetool/kernel(runtime),@nodetool/cli(CLI) - See packages/AGENTS.md for full package list
- React 18.2.0, TypeScript 5.7.2, Vite 6.4.1
- MUI v7.2.0 + Emotion, Zustand 4.5.7, ReactFlow 12.10.0
- TanStack Query v5.62.3, React Router v7.12.0
- Jest 29.7.0 + React Testing Library 16.1.0, Playwright for E2E
- Electron 35.7.5, React 19.1.0, TypeScript 5.3.3
- Zustand 5.0.3, Vite 6.4.1
- React Native / Expo - See mobile/README.md