CLAUDE.mdis a symlink to this file. Do not editCLAUDE.mddirectly; editAGENTS.mdinstead.
-
src/main.rs: binary entrypoint (aoe). -
src/lib.rs: shared library code used by the CLI/TUI. -
src/cli/: clap command handlers (e.g.,src/cli/add.rs,src/cli/session.rs). -
src/tui/: ratatui UI and input handling. -
src/session/: session storage, configuration, and group management. -
src/tmux/: tmux integration and status detection. -
src/process/: OS-specific process handling (macos.rs,linux.rs). -
src/docker/: Docker sandboxing and container management. -
src/git/: git worktree operations and template resolution. -
src/server/: web dashboard backend (axum server, REST API, WebSocket PTY relay, auth). -
src/update/: version checking against GitHub releases. -
web/: React + TypeScript frontend for the web dashboard (built with Vite + Tailwind CSS). -
src/migrations/: versioned data migrations for breaking changes (see below). -
tests/: integration tests (tests/*.rs). -
tests/e2e/: end-to-end tests exercising the fullaoebinary (see E2E Tests below). -
docs/: user-facing documentation and guides. -
scripts/: installation and utility scripts. -
xtask/: build automation workspace. -
contrib/: community-maintained integration files (e.g., OpenClaw skill). Checked bycargo xtask check-skillin CI.
cargo build/cargo build --release: TUI-only (release binary attarget/release/aoe).cargo build --profile dev-release: optimized local builds without LTO; faster compile. Use--releasefor CI.cargo build --features serve: includes the web dashboard (needs Node.js + npm).cargo test: unit + integration tests (some skip iftmuxunavailable).cargo fmt+cargo clippy: run before pushing; fix clippy warnings unless there's a strong reason not to.- Debug logging:
AGENT_OF_EMPIRES_DEBUG=1 cargo run(writesdebug.login app data dir). - Running from source needs
tmuxinstalled.
- Stack: React 19, TypeScript, Vite, Tailwind v4, xterm.js v6. Installable as a PWA ("Install Agent of Empires" in Chrome; "Add to Home Screen" on iOS).
- Build:
cargo build --features serve(build.rs runsnpm install && npm run buildinweb/when inputs change). - Run:
aoe serve --host 0.0.0.0(token-based auth by default). - Frontend dev:
cd web && npm run devfor Vite HMR; the Rust server must also be running for API/WebSocket requests. - TUI-only
cargo build(without--features serve) needs no JS tooling.
Every configurable field must be editable in the settings TUI. When adding one to SandboxConfig, WorktreeConfig, etc., also: add a FieldKey in src/tui/settings/fields.rs; add a SettingField entry in the matching build_*_fields(); wire apply_field_to_global() + apply_field_to_profile(); add a clear_profile_override() case in src/tui/settings/input.rs; include the field in the *ConfigOverride struct in profile_config.rs with merge logic in merge_configs().
- Let
cargo fmt+cargo clippydecide; fix warnings. - No dead code. Never add
#[allow(dead_code)]or write fields/functions that nothing reads. If a field isn't used yet, don't add it; if it stops being used, remove it. - No emdashes or
--as separators in docs/comments; use commas, semicolons, or rephrase. - Rust naming:
snake_casemodules/functions,CamelCasetypes,SCREAMING_SNAKE_CASEconstants. - Keep OS-specific logic in
src/process/{macos,linux}.rs, not sprinkledcfgchecks. - Don't preserve backwards compatibility by default; call it out when a change is breaking.
- Comments: explain non-obvious "why"; skip section headers and comments that restate the code.
- Use unit tests in-module (
#[cfg(test)]) for pure logic; usetests/*.rsfor integration tests. - Tests must be deterministic and clean up after themselves (tmux tests should use unique names like
aoe_test_*oraoe_e2e_*). - Avoid reading/writing real user state; prefer temp dirs (see
tempfileusage insrc/session/storage.rs). - New features touching TUI rendering, CLI subcommands, or session lifecycle should consider adding an e2e test.
Full-binary e2e tests live in tests/e2e/, exercising aoe through tmux (TUI) and as a subprocess (CLI). Run with cargo test --test e2e (add -- --nocapture for screen dumps on failure).
The harness (tests/e2e/harness.rs) exposes TuiTestHarness with spawn_tui()/spawn(args), send_keys(keys)/type_text(text), wait_for(text) (10s timeout), capture_screen()/assert_screen_contains(text), and run_cli(args). TUI tests auto-skip without tmux; Docker tests use #[ignore]; all use #[serial] for tmux isolation.
Recording (for PR reviews): RECORD_E2E=1 cargo test --test e2e -- --nocapture locally (needs asciinema + agg, outputs to target/e2e-recordings/), or add the needs-recording label in CI.
Baseline Playwright tests live in web/tests/ (run with cd web && npx playwright test). For any change to touch events, xterm.js integration, mobile-viewport behavior, WebSocket message shape, or the soft keyboard, write a real-session e2e against a running aoe serve rather than relying on the user to verify on their phone.
Recipe:
- Shim a fake tool on
$PATH(a script namedclaudethat execsbash -i) soaoe add --cmd claudecreates a live tmux session. cargo build --features serve, then startaoe serve --no-auth --port Nin the background with isolatedHOME=/tmp/aoehome.- In a Node script using
@playwright/test(already a devDep), emulate mobile withdevices['iPhone 13']sopointer: coarsematches. - Spy on PTY bytes by patching
WebSocket.prototype.sendin anaddInitScriptand pushing intowindow.__WS_SENT__. - Synthesize multi-touch via
page.evaluatedispatching rawnew TouchEvent(...)on.xterm— Playwright'spage.touchscreenis single-finger only.
Keep the script ephemeral unless promoted to web/tests/ with a mobile Playwright project (hasTouch: true, isMobile: true). Install browser deps with npx playwright install-deps if missing.
Gotcha: synthetic touchmove events fire back-to-back with Δt≈1ms, which blows up any Δpx / Δtime velocity calculation. Cap velocity and per-frame emit counts, or a real device will look sane while the e2e produces runaway momentum (or vice-versa).
- Branch names:
feature/...,fix/...,docs/...,refactor/.... - Commit messages: use conventional commit prefixes (
feat:,fix:,docs:,refactor:). - PRs: follow the template in
.github/pull_request_template.md. When creating PRs viagh pr create, read the template first and use its structure for the--bodyargument. Include a clear “what/why”, how you tested (cargo test, plus any manual tmux/TUI checks), and screenshots/recordings for UI changes.
- Do not modify git configuration (e.g.,
.gitconfig,.git/config,git configcommands) without explicit user approval. - The one exception: adding a new remote to fetch a contributor's fork during PR code review is allowed without asking.
- Runtime config/data location:
- Linux:
$XDG_CONFIG_HOME/agent-of-empires/(defaults to~/.config/agent-of-empires/) - macOS/Windows:
~/.agent-of-empires/
- Linux:
- Keep user data out of commits. For repo-local experiments, use ignored paths like
./.agent-of-empires/,.env, and.mcp.json. aoe servewrites several files to the app dir while running. All are owner-only (0600) where they contain secrets. The daemon cleans them up on shutdown;daemon_pid()'s stale-PID check sweeps them otherwise.serve.pid: daemon PID for--stopand reattach detection.serve.url: primary URL (includes the auth token) plus alternates.serve.mode:tunnel/tailscale/local.serve.log: daemon stdout/stderr tail.serve.passphrase: plaintext Tunnel passphrase, so the TUI can show it on reopen across restarts.serve.last_mode,serve.last_port: picker defaults across launches.
Breaking changes to stored data (file locations, config schema) go through src/migrations/, not inline fallback/compat shims. A .schema_version file tracks state; migrations::run_migrations() runs pending ones in order on startup and bumps the version.
To add one:
- Create
src/migrations/vNNN_description.rswith apub fn run() -> anyhow::Result<()>. - In
src/migrations/mod.rs: addmod vNNN_description;, bumpCURRENT_VERSION, append aMigration { version: NNN, name: "description", run: vNNN_description::run }entry.
Migrations must be idempotent, use tracing::info!, gate platform-specific ones with #[cfg(target_os = "...")], and be tested by hand-crafting the old state.
docs/cli/reference.md is auto-generated by cargo xtask gen-docs; edit the clap help in src/cli/ and re-run instead. CI enforces it.
The public website (agent-of-empires.com) is an Astro static site in website/.
docs/is the canonical source for all documentation and guide content. Edit docs here, never on the website side.- Astro component pages (
*.astro) likewebsite/src/pages/guides/index.astroare not generated; edit them directly.
Adding a new page to the website:
- Create the page in
docs/(with a# Titleas the first line). - Add an entry to the
PAGESarray inwebsite/scripts/sync-docs.mjswithsource,dest,title, anddescription. - Add the page's source path → website URL mapping to
URL_MAPin the same script. - Add a nav entry in
website/src/data/docsNav.ts.
The CI workflow (.github/workflows/docs.yml) triggers on changes to docs/**, website/**, and other relevant paths.
Read DESIGN.md before any visual/UI change — fonts, colors, spacing, and aesthetic direction are defined there. Don't deviate without explicit approval; in QA mode, flag code that doesn't match.
When the user's request matches an available skill, ALWAYS invoke it using the Skill tool as your FIRST action. Do NOT answer directly, do NOT use other tools first. The skill has specialized workflows that produce better results than ad-hoc answers.
Key routing rules:
- Product ideas, "is this worth building", brainstorming → invoke office-hours
- Bugs, errors, "why is this broken", 500 errors → invoke investigate
- Ship, deploy, push, create PR → invoke ship
- QA, test the site, find bugs → invoke qa
- Code review, check my diff → invoke review
- Update docs after shipping → invoke document-release
- Weekly retro → invoke retro
- Design system, brand → invoke design-consultation
- Visual audit, design polish → invoke design-review
- Architecture review → invoke plan-eng-review
- Save progress, checkpoint, resume → invoke checkpoint
- Code quality, health check → invoke health