Minimal operating guide for AI coding agents in this repo.
- Classify task type:
- Info-only (triage/review/questions/docs guidance): no code edits and no test runs unless explicitly requested.
- Code change: make minimal scoped edits and run only required checks from Testing Matrix.
- State assumptions explicitly. If uncertain, ask.
- If the task touches tooling/builds/linting, read
package.jsonandtsconfig*.jsonbefore source files. - Prefer repo scripts over reconstructing command bundles by hand:
pnpm check:quick: lint + typecheckpnpm check:tooling: lint + typecheck + buildpnpm check:unit: unit + smokepnpm check: full non-integration validation
- Read at most 3 files first:
- owning handler/module
- one shared helper used by that handler
- one downstream platform file if needed
- Define verifiable success criteria before editing.
- Decide docs/skills impact up front.
- Solve issues with the smallest context read.
- Keep changes scoped to one command family or module group.
- Preserve daemon session semantics and platform behavior.
- Expand only when contracts cross module boundaries.
- Do not read both iOS and Android paths unless explicitly cross-platform.
- If requested fix expands beyond one command family/module group, stop and confirm before broadening scope.
- Minimum code that solves the problem. No speculative features.
- No abstractions for single-use code.
- Surgical edits only.
- Match existing style.
- Remove imports/variables YOUR changes made unused; do not clean unrelated dead code.
- Keep tests minimal: if TypeScript can enforce a contract or invalid shape, prefer a type-level check over duplicating that assertion in runtime tests.
- Keep modules small for agent context safety:
- target <= 300 LOC per implementation file when practical.
- if a file grows past 500 LOC, plan/extract focused submodules before adding new behavior.
- exception: generated files, schema/fixture snapshots, and integration test aggregations.
- Keep
src/daemon.tsas a thin router. - Keep command names and daemon routing groups centralized in
src/command-catalog.ts; do not re-create command string sets in handlers or request policy modules. - Keep CLI/client positional grammar in
src/command-codecs.tsand itssrc/command-codecs/*command-family modules. CLI commands, typed client methods, and daemon interaction adapters should reuse these codecs instead of duplicating selector/ref/positionals parsing. - Keep
src/daemon/request-router.tsas request orchestration: auth, diagnostics scope, request admission, locking, handler chain, and fallback dispatch. - Put request policies in focused request modules:
- tenant/lease/selector/lock admission:
src/daemon/request-admission.ts - artifact/error finalization:
src/daemon/request-finalization.ts - Android ADB provider scoping:
src/daemon/request-android-adb.ts - generic fallback dispatch + action recording:
src/daemon/request-generic-dispatch.ts - recording invalidation health:
src/daemon/request-recording-health.ts
- tenant/lease/selector/lock admission:
- Put command logic in handler modules:
- session/apps/appstate/open/close/replay/logs:
src/daemon/handlers/session.ts - click/fill/get/is:
src/daemon/handlers/interaction.ts - snapshot/wait/alert/settings:
src/daemon/handlers/snapshot.ts - find:
src/daemon/handlers/find.ts - record/trace:
src/daemon/handlers/record-trace.ts
- session/apps/appstate/open/close/replay/logs:
- Generic passthrough (press/scroll/type) is daemon fallback only after handlers return null.
- Package manager:
pnpmonly. Do not add or restorepackage-lock.json. - Runtime baseline is Node >= 22. Prefer built-in Node APIs such as global
fetch, Web Streams, andAbortSignal.timeoutover compatibility wrappers unless the surrounding code needs a lower-level transport. - Lint/format stack is OXC:
- config:
.oxlintrc.json,.oxfmtrc.json
- config:
- TypeScript is strict enough to surface dead code early:
strict,isolatedModules,noUnusedLocals, andnoUnusedParametersare enabled. - The repo emits with
rslib, nottsc. If declaration generation fails, inspecttsconfig.lib.jsonfirst. tsconfig.lib.jsonneeds an explicitrootDir: "./src"for declaration layout.server.jsonMCP registry metadata must stay in sync withpackage.jsonversionandmcpName; runpnpm sync:mcp-metadataafter changing either field, and rely onpnpm check:tooling/CI to verify it.- Use the aggregate scripts in
package.jsonwhen possible; they encode the expected validation bundles better than ad hoc command lists.
- Prefer these first-pass commands over broader reads:
rg -n "<symbol|command|flag>" src testrg --files src/daemon/handlers src/platforms/ios src/platforms/androidgit diff -- <path>for active-branch context- read
.oxlintrc.jsonbefore treating lint output as source-level bugs
- If build/type errors mention declaration generation, inspect
tsconfig.lib.jsonbefore reading platform code. - If lint failures appear after toolchain edits, check whether the rule is from
eslint/*,typescript/*,import/*, ornode/*in.oxlintrc.jsonbefore assuming source bugs.
logs:src/daemon/handlers/session.ts->src/daemon/app-log.ts->src/daemon/handlers/__tests__/session.test.tsopen/close/replay/apps/appstate:src/daemon/handlers/session.ts->src/daemon/session-store.ts->src/daemon/handlers/__tests__/session.test.tsclick/fill/get/is:src/daemon/handlers/interaction.ts->src/daemon/selectors.ts->src/daemon/handlers/__tests__/interaction.test.tssnapshot/wait/settings/alert:src/daemon/handlers/snapshot.ts->src/daemon/snapshot-processing.ts->src/daemon/handlers/__tests__/snapshot-handler.test.tsrecord/trace:src/daemon/handlers/record-trace.ts->src/platforms/ios/runner-client.ts->src/daemon/handlers/__tests__/record-trace.test.ts
- Keep dependency direction clean:
runner-client.ts: command execution + retry behaviorrunner-transport.ts: connection/probing/HTTP transportrunner-contract.ts: sharedRunnerCommandtype and runner connect/error helpersrunner-session.ts: session lifecycle and request/response executionrunner-xctestrun.ts: xctestrun preparation/build/cache logic
runner-transport.tsmust not import back fromrunner-client.ts.- If changing runner connect errors, retry policy, or command typing, start in
src/platforms/ios/runner-contract.tsbefore touching client/transport files.
A new snapshot/command flag touches up to 7 files in a fixed order. Follow this checklist:
src/utils/command-schema.ts: add toCliFlagstype,FLAG_DEFINITIONSarray, and the relevant*_FLAGSconstant (e.g.SNAPSHOT_FLAGS). Update the command'susageOverridestring.src/utils/snapshot.ts(or the relevant options type): add toSnapshotOptionsor equivalent.src/client-types.ts: add toCaptureSnapshotOptions(or equivalent public options type) andInternalRequestOptions.src/client-normalizers.ts: map the public option name to the internal flag name inbuildFlags.src/daemon/context.ts: add toDaemonCommandContexttype andcontextFromFlagsfunction.src/core/dispatch-context.ts: add toDispatchContextwhen the flag flows into platform dispatch, then thread it through the relevant dispatcher module.src/cli/commands/<command>.ts: pass the flag fromflags.*to the client call.
Command-only flags (like find --first) that don't flow to the platform layer only need steps 1 and the handler file.
- Use process helpers from
src/utils/exec.tsfor TypeScript process execution:runCmd,runCmdStreaming,runCmdSync,runCmdBackground, andrunCmdDetached. Do not import rawspawn/spawnSyncoutsidesrc/utils/exec.ts; add or extend an exec helper instead. Plain.mjspackaging fixtures that cannot import TypeScript helpers should keep child-process usage local and preferexecFile/execFileSyncover spawn. - Use daemon session flow for interactions (
openbefore interactions,closeafter). - Use
keyboard dismissfor iOS keyboard dismissal; it may tap safe native controls such asDonebut must not fall back to system back navigation. - Do not remove shared snapshot/session model behavior without full migration.
- Command/device support must come from
src/core/capabilities.ts. - Apple-family target changes must keep
src/utils/device.ts,src/core/capabilities.ts,src/core/dispatch-resolve.ts,src/platforms/ios/devices.ts, andsrc/platforms/ios/runner-xctestrun.tsin sync. - iOS simulator-set scoping is iOS-specific: do not let
iosSimulatorDeviceSethide the host macOS desktop target when--platform macosor--target desktopis requested. - If Swift runner code changes, run
pnpm build:xcuitest. - Use
inferFillTextanduniqueStringsfromsrc/daemon/action-utils.ts. - Use
evaluateIsPredicatefromsrc/daemon/is-predicates.tsfor assertion logic.
- Logs backend/source of truth is
src/daemon/app-log.ts. session.tsshould orchestrate only (start/stop/path/doctor/mark), not duplicate backend logic.- Preserve external grep/tail workflow in docs/skills.
- Diagnostics source of truth:
src/utils/diagnostics.tswithDiagnosticsScope,emitDiagnostic,withDiagnosticTimer,flushDiagnosticsToSessionFile
- Do not add ad-hoc stderr/file logging where diagnostics helpers apply.
- Normalize user-facing failures via
src/utils/errors.ts(normalizeError). - Failure payload contract:
code,message,hint,diagnosticId,logPath,details. - Preserve
hint,diagnosticId,logPathwhen wrapping/rethrowing errors. --debugis canonical;--verboseis backward-compatible alias.- Keep redaction centralized in diagnostics helpers.
- Treat optional optimization calls such as cache/preflight/probe requests as best-effort unless the feature contract says they are required. If an optimization fails, times out, returns non-OK, or returns an unusable shape, prefer falling back to the existing required command path.
- Keep optimization timeouts shorter than the underlying operation timeout. A preflight should not consume the full budget for a later upload or command.
- Interaction commands (
click,fill,get,is) andwaitaccept selectors and@ref. - Pipeline: parse -> resolve -> act -> record selectorChain -> heal on replay.
- Keep selector parsing/matching in
src/daemon/selectors.ts. - Call
buildSelectorChainForNodeafter resolving target nodes. - New element-targeting interactions must support selector +
@ref, recordselectorChain, and hook replay healing (healReplayActioninsession.ts+ selector helpers insession-replay-heal.ts). - New selector keys remain centralized in
selectors.ts. - New
ispredicates belong inevaluateIsPredicate. - On macOS, snapshot rects are absolute in window space. Point-based runner interactions must translate through the interaction root frame; do not assume app-origin
(0,0)coordinates. - Prefer selector or
@refinteractions over raw x/y commands in tests and docs, especially on macOS where window position can vary across runs.
- Before writing a new test, check
src/__tests__/test-utils/for existing helpers:device-fixtures.ts: canonicalDeviceInfoconstants (ANDROID_EMULATOR,IOS_SIMULATOR,IOS_DEVICE,MACOS_DEVICE,LINUX_DEVICE, etc.)session-factories.ts:makeSession,makeIosSession,makeAndroidSession,makeMacOsSessionstore-factory.ts:makeSessionStore(creates tempSessionStoreinstances)snapshot-builders.ts:buildNodes,makeSnapshotStatemocked-binaries.ts:withMockedAdb,withMockedXcrun(stub CLI binaries for dispatch tests)
- Use
import { ... } from '<relative-path>/__tests__/test-utils/index.ts'for convenient barrel imports. - Prefer shared fixtures over inlining new
DeviceInfoorSessionStateobjects in tests. - Do not duplicate
makeSessionStore,makeSession, or device constants when a shared helper already exists.
- Docs/skills only: no tests required unless a more specific rule below applies.
- CLI help/guidance changes in
src/utils/command-schema.ts: runpnpm exec vitest run src/utils/__tests__/args.test.ts. - SkillGym prompt/assertion changes: run the touched
--casechecks. For broad validation, usepnpm test:skillgym; use--tag fixture-smokeor--tag skill-guidancewhen validating one suite group. - Non-TS, no behavior impact: no tests unless requested.
- Keep tests behavioral; do not assert shapes or cases TypeScript already proves.
- Any TS change:
pnpm typecheckorpnpm check:quick. - Tooling/config change (
package.json,tsconfig*.json,.oxlintrc.json,.oxfmtrc.json):pnpm check:tooling. - MCP registry metadata changes (
server.jsonor packageversion/mcpName): runpnpm sync:mcp-metadata, thenpnpm check:tooling. - Daemon handler/shared module change:
pnpm check:unit. - iOS runner/Swift change:
pnpm build:xcuitest. - Cross-platform behavior change: run
pnpm test:integration. - Any change in:
src/,test/,skills/:pnpm format.
- Do not read unrelated files once owning module is identified.
- Do not run integration tests by default.
- Do not inspect both iOS and Android codepaths unless task requires both.
- Prefer targeted
git diff -- <paths>over broad file reads during review. - Prefer
snapshot -i,find, and scoped selectors over repeated full snapshot dumps when exploring Apple desktop UIs. - Keep PR summaries short and scoped.
- Adding command logic to
src/daemon.tsinstead of handlers. - Adding capability checks outside
src/core/capabilities.ts. - Inlining
ispredicate logic in handlers. - Returning non-normalized user-facing errors.
- Duplicating logs backend logic in handlers instead of
src/daemon/app-log.ts. - Growing
src/daemon/handlers/session.tsorsrc/platforms/ios/apps.tsfurther without extracting Apple-family/macOS-specific helpers first. - Reintroducing an npm lockfile or assuming ESLint/Prettier still exist in this repo.
- Changing
tsconfig.lib.json/build tooling without runningpnpm check:tooling; declaration generation is stricter thantsc --noEmit.
- Versioned CLI help is the agent-facing source of truth. Put workflow guidance in
src/utils/command-schema.tshelp topics and assert important copy insrc/utils/__tests__/args.test.ts. - Skills are thin routers. Keep
skills/**/SKILL.mdfocused on when to use the skill, version gating, whichagent-device help <topic>page to read, and a short default loop. Do not duplicate full CLI manuals in skills. - For behavior/CLI surface changes, update the versioned help instructions in
src/utils/command-schema.tsand assert important help copy insrc/utils/__tests__/args.test.ts. Also updateREADME.mdand relevantwebsite/docs/**when user-facing docs need it. - For behavior/CLI surface changes and command-planning guidance changes, write or update a SkillGym case in
test/skillgym/suites/agent-device-smoke-suite.tsthat captures the expected agent command plan. - Do not update
skills/**/SKILL.mdfor command behavior or workflow guidance unless the user explicitly asks; skills must route to versioned CLI help instead of carrying behavior details. - Keep SkillGym cases behavioral and command-planning oriented. Prefer prompts that assert the user-visible contract and expected command family over brittle exact output, but forbid known bad patterns.
- Build before SkillGym when local CLI help is needed:
pnpm build, thenpnpm exec skillgym run ... --case <id>. - Run SkillGym broad validation with
pnpm test:skillgym; use v0.8--tagfilters for focused suite groups. - Preserve current high-value workflow guidance:
- iOS Expo Go dogfood: prefer
agent-device open "Expo Go" <url> --platform ioswhen the shell is known, thensnapshot -ito confirm the project UI rather than the runner splash. keyboard dismissis the preferred iOS keyboard-dismissal path before manually pressing visible keyboard controls such asDone; it remains best-effort and can report unsupported layouts explicitly.- Empty replacement is not a supported clear-field command; do not document or test
fill <target> ""as clearing. Prefer visible clear/reset controls or report the tool gap. - Mutating commands against one session must run serially. Parallelize only read-only commands or commands on separate sessions/devices.
- iOS Expo Go dogfood: prefer
- In final summaries, state whether docs/skills were updated; if not, explain why.
- If blocked by network/device/auth/permissions, stop and report:
- blocker
- why it blocks completion
- exact next command/action needed to unblock
- CLI parse + formatting:
src/bin.ts,src/cli.ts,src/utils/args.ts - Daemon client transport:
src/daemon-client.ts - Daemon state/store:
src/daemon/session-store.ts - Selector DSL and matching:
src/daemon/selectors.ts ispredicate evaluation:src/daemon/is-predicates.ts- Shared action helpers:
src/daemon/action-utils.ts - Snapshot shaping + labels:
src/daemon/snapshot-processing.ts - Handler context helpers:
src/daemon/context.ts,src/daemon/device-ready.ts - Request routing/policy:
src/daemon/request-router.ts,src/daemon/request-admission.ts,src/daemon/request-generic-dispatch.ts - Dispatcher + capability map:
src/core/dispatch.ts,src/core/dispatch-context.ts,src/core/dispatch-interactions.ts,src/core/capabilities.ts - Command catalog + positional codecs:
src/command-catalog.ts,src/command-codecs.ts,src/command-codecs/* - Platform backends:
src/platforms/ios/*,ios-runner/*,src/platforms/android/*
- Before opening PR: ensure no conflict markers/unmerged paths.
- Commit messages and PR titles should use conventional prefixes such as
feat:,fix:,chore:,perf:,refactor:,docs:,test:,build:, orci:as appropriate. - Do not use bracketed automation prefixes such as
[codex]or similar bot tags in commit messages or PR titles. - Open a ready-for-review PR by default. Use a draft PR only when the user explicitly asks for one or the work is intentionally incomplete.
- Run required checks for touched scope from Testing Matrix.
- PR body must be short and include:
## Summary## Validationwith exact commands run
- Call out known gaps/follow-ups explicitly.
- Include touched-file count and note if scope expanded beyond initial command family.
- When guidance conflicts, apply in this order: Hard Rules -> Scope -> Testing Matrix -> style/preferences.