Contributing

Thank you for helping with Atlas. This page is the long version of CONTRIBUTING.md. By taking part you agree to the Code of Conduct. Atlas is released under the MIT license, and your contributions are too.

Ways to help#

  • Report a bug or ask for a feature in the issue tracker. Include atlas version, your OS, the steps and the relevant lines of .logs/<service>.log (redact secrets).
  • Improve the docs. Every page on the website has an Edit on GitHub link; the source is docs/*.md.
  • Send a fix or a feature. For anything bigger than a small fix, open an issue first so we can agree on the shape before you spend time on it.
  • Security issues do not go in public issues. See SECURITY.md.

Set up#

You need rustup, bun and, for the desktop app, the Xcode command line tools. gh is optional.

git clone https://github.com/codeatlasdev/atlas.git
cd atlas
scripts/setup.sh        # idempotent: checks tools, installs frontend deps, cargo-tauri and cargo-deny

The Rust toolchain is pinned in rust-toolchain.toml; rustup installs it on the first cargo command. Workspace lints apply to every crate: fix warnings, do not add global allows.

The gate#

bun run check

bun run check is the only gate and the one CI runs. It chains, in order:

  1. cargo fmt --all -- --check
  2. cargo clippy --workspace --all-targets --locked -- -D warnings
  3. cargo test --workspace --locked
  4. cargo deny check (licenses, advisories, duplicate and banned crates)
  5. scripts/check-skills.sh (validates skills/*/SKILL.md against the Agent Skills spec)
  6. bun run desktop:check (svelte-check, prettier and bun test for the frontend)

Run it on its own and read the exit code before you push. A pull request should pass it.

Also useful:

CommandUse
bun run dupjscpd duplication report; keep it at or below 1.5%
cargo test -p atlas-cli --test mcpthe MCP integration suite (real binary, live headless run; needs python3)
cargo test -p atlas-tui --release -- --ignored --nocapture bench_one_millionthe log pipeline benchmark
cargo test -p atlas-term --release -- --ignored --nocapture screen_benchthe terminal renderer benchmark

Build and run your changes#

cargo build -p atlas-cli                   # builds the `atlas` binary (atlas-tui alone is only a library)
codesign -f -s - target/debug/atlas        # macOS: ad-hoc sign or Gatekeeper kills it
atlas link                                 # symlink ~/.atlas/bin/atlas to the debug build
atlas unlink                               # restore the release binary

Develop the desktop app#

bun run desktop:dev        # hot reload: Vite + the Tauri shell; Rust changes rebuild
atlas link --desktop       # make /Applications/Atlas.app your debug bundle

While linked, the app shows a Dev build badge and refuses to update over your build.

Debug builds run a bridge on ~/.atlas/desktop-debug.sock, so you (or an AI agent) can inspect the app without the mouse:

atlas desktop debug info
atlas desktop debug logs --level csp        # console, errors, CSP violations
atlas desktop debug eval 'return await window.__TAURI_INTERNALS__.invoke("projects_list")'
atlas desktop debug shot /tmp/x.png

Debug builds also expose the live stores as window.__atlas (ws, store, agents, worktrees, api, host); use them from debug eval instead of import(), which creates separate module instances. Check CSP and IPC on the bundled build (tauri://localhost), not only on Vite. After a hot reload, reload the webview before trusting socket behavior.

Code layout#

See Architecture for the crates and how the processes talk to each other. Where things go:

ChangeWhere
a new atlas subcommandcrates/atlas-cli/src/commands/
config fields, validation, the JSON Schemacrates/atlas-tui/src/config/
scheduling, health, restarts, actionscrates/atlas-tui/src/runtime/manager/
TUI widgets and keyscrates/atlas-tui/src/tui/ (the key table is keymap.rs)
MCP tools, resources, promptscrates/atlas-tui/src/mcp/
terminals, exec, filescrates/atlas-host/
terminal renderingcrates/atlas-term/
desktop backendcrates/atlas-desktop/src/
desktop UIapps/desktop/src/
agent skillsskills/<name>/SKILL.md (embedded at build time)

Conventions#

  • Tests. Prefer behavior tests: boundaries, failures and state transitions over getters. Manager tests live in runtime/manager/tests.rs and are wired by #[cfg(test)] mod tests; in mod.rs; when you split modules, check the test count does not drop. Integration tests drive the real binary.
  • Docs move with code. A change that alters a flag, a key, a config field or a behavior updates the page that documents it, in the same pull request. The TUI help and footer are generated from one keymap table; the desktop shortcuts are DEFAULT_KEYMAP in apps/desktop/src/lib/keys.ts.
  • Changelog. User-visible changes get a line under ## [Unreleased] in CHANGELOG.md. Release notes are built from it.
  • Formatting. .editorconfig applies: tabs in TypeScript, Svelte and shell; 4 spaces in Rust; 2 spaces in YAML, Markdown and JSON.
  • Commits. Short imperative subject, conventional prefixes (feat:, fix:, docs:, chore:) as in the history. Explain why in the body when it is not obvious.
  • Dependencies. Prefer the standard library and what is already in the tree. A new crate or package needs a reason in the pull request; cargo deny check must pass.

Runtime rules that came from real incidents#

Keep these when you touch the runtime (Architecture):

  • Orphan cleanup uses only the .pids file and exact PIDs; never lsof port-kill or negative PIDs.
  • Never run docker compose down or restart the docker daemon from Atlas.
  • Children: setsid, stdin(null), sh -c for commands.
  • TUI: event-stream input, dirty-flag rendering, non-blocking startup.
  • Desktop: child webviews make the main window multi-webview; use get_window("main") for window operations and get_webview("main") for the app’s own webview. Never put a secret in content loaded into the embedded browser.

Pull requests#

  1. Fork, branch from main.
  2. Make the change with its tests and doc updates.
  3. Run bun run check.
  4. Open a pull request using the template; link the issue it closes.

A maintainer will review it. Small, focused pull requests get merged faster than large ones.

CI and releases#

CI and releases are local only: both workflows are workflow_dispatch and are run on a maintainer’s machine through act (bun run ci:act, bun run release). Do not expect hosted Actions on your pull request; the maintainer runs the gate. See Releasing.