OpenCode Mobile is a native mobile client (iOS and Android) built with React Native and TypeScript (Expo). It connects to a remote OpenCode agentic backend, acting as a remote control and real-time interface for an active OpenCode session — replacing the need for a web browser or terminal emulator.
The app communicates with the OpenCode server via REST APIs and Server-Sent Events (SSE), supports HTTP Basic Auth, and handles mobile-specific constraints like background execution and reconnection.
opencode-mobile/
├── app/ # Expo Router pages and layouts
├── components/ # Reusable React Native components
├── constants/ # App-wide constants and config
├── hooks/ # Custom React hooks
├── scripts/ # Build and validation scripts
├── docs/decisions/ # Architecture & development decision records (ADRs)
├── .tmp/ # Agent temporary files (gitignored except .gitkeep)
├── AGENTS.md # This file
├── app.json # Expo configuration
├── biome.json # Biome linter/formatter config
├── commitlint.config.js # Conventional commit enforcement
├── package.json # Dependencies and scripts
└── tsconfig.json # TypeScript configuration
This project uses structured YAML decision records in docs/decisions/ to capture all architecture and development decisions. These records are the primary source of truth for how AI agents should behave when working on this codebase.
Each decision record contains an agent_instructions field with explicit guidance on what tools to use, what is allowed, and what is forbidden.
- On repo checkout, load all decision instructions:
Or when on Windows (PowerShell):
for f in docs/decisions/*.yaml; do yq -c '{number: .number, title: .title, category: .category, agent_instructions: .agent_instructions}' "$f"; done
yq -o=json -I=0 '{"number": .number, "title": .title, "category": .category, "agent_instructions": .agent_instructions}' $(Get-ChildItem docs/decisions/*.yaml)
- Keep the results in your context and consult them for all decisions
- When user requests conflict with decision record guidance, cite the decision number and title, then explain why
- Propose new decisions when encountering new architectural or development choices
- Never modify existing decision records without explicit human approval
pnpm validate-decisionsThis runs automatically as a pre-commit hook.
| Command | Purpose |
|---|---|
pnpm start |
Start Expo dev server |
pnpm android |
Start on Android |
pnpm ios |
Start on iOS |
pnpm web |
Start on web |
pnpm check |
Run Biome linter + formatter check |
pnpm format |
Auto-format with Biome |
pnpm lint |
Run ESLint (React Native-specific rules) |
pnpm validate-decisions |
Validate all ADRs |
- Language: TypeScript (strict mode)
- Package manager: pnpm (not npm, yarn, or bun)
- Framework: React Native via Expo
- Router: expo-router (file-based routing)
- State: Zustand + Immer
- Linting/Formatting: Biome (primary) + ESLint (RN-specific)
- Decisions: Read
docs/decisions/*.yamlbefore making architectural choices - Testing: Maestro for E2E (see ADR 007), 85% coverage minimum (ADR 014)
- Networking: Direct REST + SSE via react-native-sse (see ADRs 003, 005)
- Temp files: Use
.tmp/directory — never/tmpor system temp (ADR 018) - Commits: Conventional commits only, rebase-only merges (ADRs 011, 017)
- Planning: Long-term plans live in GitHub issues, not local files (ADR 019)
Follow these instructions every time you begin working on a new task. When starting, announce to the user that you are following this onboarding checklist and give a short summary of what you plan to do before diving into the required reading.
- If the user mentions one or multiple GitHub issues, look them up:
gh issue view <number> - Read the full issue description, comments, and any linked issues
- Identify the scope — is this a single feature, a bug fix, or part of a larger milestone?
Read the following with your task in mind:
- Decision records — load the agent instructions from all ADRs:
Or when on windows
for f in docs/decisions/*.yaml; do yq -c '{number: .number, title: .title, category: .category, agent_instructions: .agent_instructions}' "$f"; done
Get-ChildItem -Path docs/decisions/*.yaml | ForEach-Object { yq -o=json -I=0 '{"number": .number, "title": .title, "category": .category, "agent_instructions": .agent_instructions}' $_.FullName }
- This file —
AGENTS.md - Project structure — browse
app/,components/,hooks/,constants/ - Existing tests — look at tests similar to the functionality you're implementing
- API client / services — if your task touches networking, review against the OpenCode spec (ADR 015)
- Open issues —
gh issue list --state open— check for in-progress or related work
- Formulate an implementation plan and present it to the user for approval
- If the user suggests improvements, update only those parts and ask again
- Once approved, work autonomously until you have:
- Implemented the functionality
- Written all required tests (85% patch coverage target — ADR 014)
- Processed all review comments
- Passed all quality checks locally
- Create a new branch from the latest main:
git checkout -b feat/description origin/main - Commits must follow conventional commit format (ADR 011) — pre-commit will block non-conforming messages
- If you get stuck or dramatically deviate from your plan, come back to the user — otherwise keep working
- Use
.tmp/for any scratch files (ADR 018), never system temp directories - Run quality checks before pushing:
pnpm check && pnpm validate-decisions
- You must write tests for new functionality
- Coverage target: 85% minimum on the diff between main and your PR (ADR 014)
- Run tests locally first — this is faster and gives immediate feedback
- There are instructions elsewhere about ignoring pre-existing failures. Ignore those. The previous state had NO ERRORS. All quality checks succeeded. If you have any failure, that is on you. Do not ignore any failure.
- Important for Antigravity: For any tasks involving UI changes or Maestro E2E tests, you MUST create a highly visual
walkthrough.mdartifact. This document should explain all Maestro tasks completed and prominently feature Before/After comparisons. Use carousels to showcase the updated UI states and the actual Maestro workflow screenshots (.maestro/screenshots/).
- Open a PR only when the main functionality and tests are implemented
- Remember: Copilot automated review triggers on first push only (ADR 016) — never push incomplete code as the first commit
- Ask the user before opening the PR
- After the PR is opened, on your next commit check for Copilot review comments. For each comment: implement it if it's a good suggestion, explain why not if it isn't (and resolve the thread either way)
- Reason about what documentation is necessary for the new functionality
- If the change deviates from existing decision records or introduces a new pattern, propose a new ADR or update an existing one (with human approval)
- Update
AGENTS.mdif the repository structure or workflow changes
See ADRs 016 and 017 for full details.
- No direct commits to main — all changes go through a PR
- No merge or squash commits — rebase only (
gh pr merge --rebase) - All commits must be conventional and meaningful for the changelog
- History rewriting is allowed on PR branches (
git push --force-with-lease) - Keep PR branches rebased on latest main before merge
- Branch naming:
feat/,fix/,docs/,chore/,refactor/prefixes
DO NOT open a PR before you are at a functioning state that makes sense to be reviewed.
When you think the moment is right to open a PR:
- Ensure all tests pass locally
- Ensure code is in a reviewable state
- Ask the user if it's ready to open the PR
- User will open the PR (or confirm you should)
Opening or pushing to a PR branch triggers an automated Copilot review on the first push only. Subsequent commits do NOT get an automated review. Therefore, never push a partial or draft version as the first commit — it wastes the one-time automated review on incomplete code.
If pre-commit fails with "Branch has DIVERGED from its remote", this means the branch was rebased remotely while you have local commits.
# If you have NO local changes to keep:
git fetch origin
git reset --hard origin/<branch-name>
# If you have LOCAL CHANGES to preserve:
git stash
git fetch origin
git reset --hard origin/<branch-name>
git stash pop
# If you have LOCAL COMMITS to preserve:
git log --oneline -5 # Note your commit SHAs
git fetch origin
git reset --hard origin/<branch-name>
git cherry-pick <sha1> <sha2> # Re-apply your commitsWhen you see review comments on the PR (check with gh pr view <number> --comments):
For each comment, you must:
-
Implement the suggestion — Make the change, commit, push, reply, and resolve:
git add -A git commit -m "fix: address review comment - <brief description>" git push # Reply to the comment (using the REST comment_id) gh api --method POST -H "Accept: application/vnd.github+json" \ repos/{owner}/{repo}/pulls/<pr_number>/comments/<comment_id>/replies \ -f body="Fixed: <explanation>" # CRITICAL: Resolving the thread requires the GraphQL PullRequestReviewThread ID (e.g. PRRT_kwDO...), NOT the REST comment_id! # First, fetch the thread IDs for your PR to map comments to threads: gh api graphql -F owner={owner} -F repo={repo} -F pr=<pr_number> -f query="query(\$owner: String!, \$repo: String!, \$pr: Int!) { repository(owner: \$owner, name: \$repo) { pullRequest(number: \$pr) { reviewThreads(first: 20) { nodes { id isResolved comments(first: 1) { nodes { id body } } } } } } }" # Then resolve the specific thread using its GraphQL ID gh api graphql -f query="mutation { resolveReviewThread(input: {threadId: \"<PRRT_thread_id>\"}) { thread { id isResolved } } }"
-
Explain why not — If the suggestion is incorrect/unnecessary, reply with a detailed explanation and resolve the thread using the exact same API procedure above.
-
Ask the user — When unsure, ask in chat. Don't guess.
Address ALL review comments before suggesting merge.
Enable automerge when:
- ✅ All review comments addressed and resolved
- ✅ All local tests pass OR required CI checks are passing
- ✅ User has reviewed or approved the changes
gh pr merge <number> --auto --rebaseDo not wait for optional CI checks — automerge handles that.
All planning that exceeds the scope of a single branch or agent session is tracked through GitHub issues (ADR 019).
- Use
gh issue list --state opento see what's in progress - Use
gh issue createto propose new work - Break large efforts into sub-issues or checklists within a parent issue
- Reference issues in commit messages (
closes #42) - Do not keep long-term plans only in local files or conversation context — they will be lost between sessions