Development Reference

August 18, 2026 · View on GitHub

Source development is orchestrated from the repository root with bun dev. The installed/product synergy CLI is a different surface.

Requirements and Preparation

The root manifest pins Bun 1.3.14. Install that version, then run:

bun dev prepare

Preparation installs dependencies, generates OpenAPI/SDK artifacts, builds the plugin SDK and Web app, and prepares the platform sandbox helper where supported. Linux and Windows helper compilation requires Rust; Linux sandboxing also uses Bubblewrap. build-helper.ts --local installs the local helper without editing tracked trust hashes. Stable builds compile each release helper first and embed its SHA-256 into the matching runtime binary.

Development Modes

bun dev server
bun dev app --open
bun dev web
bun dev desktop
bun dev desktop --managed
bun dev send "your message"
bun dev build app
bun dev build desktop
ModeProcesses
serversource server on fixed development port 4096 by default
appVite app against an existing server; default app port 3000
websource server, Vite app, and remote Browser Host
desktopsource server, Vite app, and Electron in external-server mode
desktop --managedbuild plugin/app, then Electron with production-style managed server mode
sendone-off source CLI execution

Use --port for standalone server and app modes. Use --server-port, --app-port, --hostname, and --attach for combined Web or Desktop flows. --hostname binds both the source server and Vite app in Web and external Desktop modes. The orchestrator checks required ports and target health before starting dependent processes. Parallel and serial modes terminate complete descendant process trees, including nested package scripts that create another process group, so stopping the orchestrator does not leave servers or Electron hosts running.

Managed Desktop rebuilds the Web distribution before launch so packaged-server behavior is not tested against stale frontend assets. Its server also watches the Electron parent and shuts down if Electron is force-terminated without running normal quit handlers. Normal daily Desktop work should use external mode for Vite reload speed.

Vite development and App builds launched through bun dev identify themselves as local@<git-revision> (with +dirty when applicable) on fatal and startup error pages and emit source maps for diagnostics. The semantic package version remains unchanged for update compatibility. Direct package and release builds keep the package version label and do not emit source maps by default.

Managed Desktop resolves the current user's login shell once during Desktop startup and imports only a normalized PATH into the managed server environment. Login-shell entries take precedence while inherited absolute entries remain as fallbacks; other profile variables and output are discarded. If the probe fails, Desktop keeps its inherited PATH. The Bash tool still executes commands with the ordinary non-login shell -c path and the existing sandbox environment allowlist. Developer-mode Settings → Observability shows the effective Desktop PATH, its source, and fixed command-resolution diagnostics.

Managed Desktop always starts its private Synergy server with --hostname 127.0.0.1. Plugins with approved runtime.endpoint.read access may read that credential-free loopback origin; the Host Service serves loopback and wildcard binds over loopback and fails closed only when the listener excludes loopback.

The development orchestrator tags each spawned command so shutdown can recover descendant process groups after an intermediate package wrapper exits. Managed servers arm cross-platform parent-process liveness monitoring before startup work can report healthy.

Developing Synergy with Synergy

Do not restart or stop the Synergy instance carrying your current session. Run the source checkout against an isolated home and explicit alternate ports:

mkdir -p /tmp/synergy-dev/.synergy
cp -r ~/.synergy/config /tmp/synergy-dev/.synergy/config

SYNERGY_HOME=/tmp/synergy-dev \
  bun dev web --server-port 4097 --app-port 3001

The config copy preserves provider setup while SYNERGY_HOME isolates data, logs, daemon state, and locks. Never run synergy stop against the main instance as part of a source test.

Tests

Core runtime tests run from packages/synergy:

cd packages/synergy
bun test
bun run test:ci
bun test test/tool/read.test.ts
bun run test:changed
bun run test:coverage
bun test --watch

bun run test:ci matches the CI core-suite boundary: four sequential shards run in separate Bun processes, limiting process-global state and temporary fixture accumulation while avoiding concurrent port and environment collisions. Set SYNERGY_TEST_JUNIT_DIR to a package-relative directory when per-shard JUnit reports are needed.

bun run test:coverage runs through script/coverage-run.ts, which spawns every coverage batch with an injected isolated test home. Both orchestrators (test-ci.ts, coverage-run.ts) set SYNERGY_TEST_HOME/SYNERGY_TEST_ROOT in the child environment and delete SYNERGY_HOME, because Bun 1.3.x does not propagate test/preload.ts environment into --parallel worker processes — a raw bun test --coverage --parallel run would fall through to the real user home and write fixtures into ~/.synergy/data.

For the full isolation procedure, the guard predicate, and the escape hatch, see the testing-guide Skill (.synergy/skill/testing-guide/SKILL.md). Run core suites through the package scripts; the only escape hatch for a deliberate real-home test run is SYNERGY_ALLOW_REAL_HOME=1 (see Configuration layout).

The same pinned catalog is the default input for core binary builds. packages/synergy/script/models-catalog.ts validates it and requires non-empty OpenAI, Anthropic, and Google entries before compilation; MODELS_DEV_API_JSON can override the input for an ordinary local build, while release builds always force the repository-pinned snapshot.

Other package tests can run through Turbo or their package scripts:

bun turbo test
bun run desktop:test
bun run --cwd packages/app test
bun run --cwd packages/ui test

Every test file lives under the owning package's test/ directory; repository script and policy tests live under the root test/ directory. Mirror source-domain subdirectories where useful, but never colocate *.test.* or *.spec.* beside implementation files. Run bun run test-layout:check to validate the repository boundary.

The App and shared UI package scripts are part of the Turbo test graph. The App runner isolates its production CSS build contract from ordinary unit tests. The UI runner executes ordinary suites together and isolates the session-turn timeline suite so its global module mocks cannot contaminate neighboring tests.

Theme source changes also regenerate the static boot fallback, Tailwind color mappings, and JSON schema from the canonical token catalog:

bun run --cwd packages/ui generate:theme

Frontend product colors must use the canonical semantic theme contract. Do not add Tailwind palette colors, arbitrary literal color utilities, or component-local light/dark palettes. See Frontend themes and color for consumer rules, imperative renderer integration, semantic token changes, and the recommended plugin workflow for creating a selectable theme.

Localized frontend changes also update and validate the shared App/UI Lingui catalog:

bun run --cwd packages/app i18n:extract
bun run localization:check

i18n:extract is App-owned because packages/app/lingui.config.ts includes both packages/app/src and packages/ui/src. The single root localization:check gate re-extracts catalogs and rejects drift, rejects missing or blank Simplified Chinese translations, strictly compiles every locale, then enforces the App/UI source contract for hard-coded visible strings, Chinese source literals, hard-coded locale tags, invalid descriptors, dynamic IDs, and Lingui macro imports. Keep tracked PO catalogs unchanged after extraction before handing off a localization change.

Write behavior tests around public invariants. Avoid source-text assertions that fail when an implementation is refactored without changing behavior.

Quality Commands

bun run format:check
./script/format.ts
bun run lint
bun run lint:fix
bun run typecheck
bun run deadcode
bun run monorepo:check
bun run workflow:check
bun run secrets:check
bun run package:check
bun run test-layout:check
bun run quality:quick
bun run quality

quality:quick is the default local PR preflight: format, lint, Skill/package-guide/test-layout checks, strict localization contract, typecheck, monorepo consistency, and package validation. quality adds all Turbo tests. The pre-push hook runs Bun-version, format, lint, typecheck, and monorepo checks; CI runs the wider matrix.

See Open-source quality for exact jobs and failure guidance.

SDK and Builds

After modifying server routes or OpenAPI-visible schemas:

./script/generate.ts

Build the core single binary/runtime artifact with:

./packages/synergy/script/build.ts --single

The build validates and embeds the pinned test/tool/fixtures/models-api.json catalog before compiling. Use MODELS_DEV_API_JSON=/path/to/models.json only for a deliberate local build override; release workflows ignore that override and embed the repository snapshot.

Frontend code should use createSynergyClient() and generated methods for internal routes. Raw browser transports remain appropriate for WebSocket/EventSource streams, external URLs, browser file/blob handling, and endpoints whose semantics cannot be represented by the SDK.

Repository Workflow

The development branch is dev; main is updated by the release workflow. All pull requests target dev.

Repository changes may be made directly in the current checkout. Treat primary and pre-existing checkouts as potentially shared: preserve unrelated work, and do not switch branches or rebase unless the user explicitly requests it. Use a task-owned worktree when concurrent development needs branch or file isolation, not as a prerequisite for every edit, and reuse an existing task worktree when it already owns the branch.

Stage and commit only when requested. Local commits on dev or main are allowed, but never push either protected branch directly. Push a topic branch and open its pull request against dev; the release workflow is the only path from dev to main. Preserve unrelated changes, stage explicit task files, and keep local paths, runtime identifiers, logs, credentials, and internal configuration out of commit and GitHub text. See git-guide for the commit template, mandatory agent co-author footer, and publication workflow.

Keep changes focused, update migrations for persisted schema changes, and update current-state docs plus relevant .synergy/skill workflows whenever an agent-facing command, path, tool, or procedure changes.

Development workflow ownership

The repository-local .synergy/skill/ directory is the executable handbook for changing Synergy itself. It is distinct from user-installed Skills and public plugin documentation.

ChangeOwning workflow
Unclear or cross-cutting source change; new reusable conventiondevelopment-standards
Architecture tracing and impact analysisarchitecture
Web or shared product UI, including semantic iconsdevelop-frontend
Internal LLM call, model-backed classifier, existing session invocation, or delegated child taskintegrate-llm
HTTP route, OpenAPI schema, generated SDK, or internal Web API callchange-server-api
Durable storage, schema, index, migration, or recoverychange-persistence
Capability, permission, control-profile, enforcement, or sandbox behaviorchange-execution-boundaries
Browser ownership/control, Desktop native presentation, or WebRTCchange-browser-runtime
Plugin manifest, install/update, runtime, bridge, marketplace, or UI hostchange-plugin-runtime
Built-in agent, CLI command, or first-party tooladd-agent, add-cli-command, or add-tool
Test selection, isolated runtime, or Git operationtesting-guide, develop-synergy, or git-guide
Broad simplification audit, dead/duplicated/over-built surface, or decision-record coalescingfind-simplifications

When implementation or review reveals a reusable required pattern, registration, safety constraint, or verification step, update the owning Skill in the same change. Create a focused verb-led Skill if no existing workflow would reliably trigger. Root and package AGENTS.md files retain safety, global invariants, and routing; canonical docs retain product and architecture truth; Skills retain the executable procedure.

Area-Specific Checks

  • Server route/schema: regenerate SDK, typecheck runtime and SDK, run affected route tests.
  • Session/message loop: read the session and frontend-sync contracts, run focused session tests, then shared checks.
  • Frontend: typecheck packages/app, run relevant UI tests, preserve generated SDK usage and PRODUCT.md principles.
  • Desktop/release: run Desktop typecheck/tests/build validation and review the Desktop release runbook.
  • Plugin/public package: build package, run package:check, and verify exported ESM/type paths.
  • Config/auth examples: run secret scanning and verify both global and project configuration behavior.