You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Claude Code chat is retired. OpenCode is the only chat runtime (OpenCodeClient, OpenCode*Service, CodingAgentRuntimeKind.openCode).
Claude → OpenCode migration runs automatically per project via ClaudeToOpenCodeMigrationService on chat open / send / runtime apply: promotes runtime, clears proxy transport anchors, copies compatible API keys, soft-copies CLAUDE.md → AGENTS.md when missing, imports .mcp.json into opencode.json(c), ensures session + managed scheduler MCP, stamps openCodeMigrationVersion.
Nil/unknown agentRuntimeRawValue resolves to OpenCode (not Claude).
CodingAgentRuntimeRegistry always returns OpenCodeRuntimeService (legacy .claudeProxy enum case remains for decode/migration only).
Agent daemon (:8787, ProxyTaskService, server/claude-proxy) remains for tasks/push/scheduled runs until rehomed — do not delete those paths when cleaning chat.
Daemon Anthropic auth preference lives in AgentDaemonAuthService (not Claude chat).
Do not expand Claude Code chat UX. MCP UI/management is OpenCode-primary (CodingAgentMCPService → OpenCodeMCPService).
Chat must not import ClaudeCodeService. Residual CCS usage is tasks/installer/legacy settings only until retirement.
ChatViewModel module map
File
Responsibility
ChatViewModel.swift
Lifecycle, configure, public API, thin orchestration
ChatViewModel+OpenCodeSend.swift
Send / stream / abort
ChatViewModel+OpenCodeHydration.swift
Hydrate + session reconcile
ChatViewModel+MCP.swift
MCP cache / fetch / deferred
ChatViewModel+ToolApproval.swift
Tool permission + OpenCode questions
ChatViewModel+Persistence.swift
Load/save messages
ChatViewModel+MediaPrefetch.swift
CodeAgents UI media prefetch
ChatViewModel+DeferredStartup.swift
Post-ready MCP/rules/media queue
ChatViewModelSupport.swift
Pure helpers (merge, planners)
Runtime service files
File
Responsibility
CodingAgentRuntimeTypes.swift
Kind, selection store, protocol
OpenCodeRuntimeService.swift
OpenCode health/send/hydrate/permissions
CodingAgentRuntimeRegistry.swift
OpenCode-only resolution
AgentDaemonAuthService.swift
Daemon Anthropic API key vs token preference
Retired Claude chat stack
ClaudeCodeService deleted. Shared leftovers still used by OpenCode/tasks: MessageChunk, LineBuffer.
ClaudeNotInstalledView deleted. Use OpenCodeUnavailableView for chat gates.
Task credentials still use ClaudeProviderSettingsView via AIProviderSettingsMode.claudeProxy labeled Task Provider.
ProxyStreamClient is not a chat client: only fetchCanonicalConversationId remains for tasks on :8787. Chat stream/replay/permission methods removed.
Testing principles (always apply)
Do not add app/production code solely so tests pass. No --ui-testing / MOBILECODE_E2E_* branches, secret seeding, auto-presented sheets, extra invisible buttons, or other harness-only paths inside MobileCode/ unless they are real product features users need.
Tests exist to make the app better, not to greenwash CI. Prefer fixing real UX/reliability (hittable controls, correct defaults, clearer errors, timeouts/retries that help users) over teaching the app to cheat for XCTest.
Put harness logic in MobileCodeTests/, MobileCodeUITests/, and scripts/e2e/ (launch args only as inputs the test uses, not behavior forks in the app).
Existing evidence/seed helpers under UI-testing flags are legacy; do not add more. Prefer deleting or narrowing them when touching that code.
When a UI test is flaky, fix test interaction (waits, scrolling, coordinates) or product accessibility/layout that helps everyone — not a test-only overlay.
screenshots/: Visual assets used in README and PRs
VERSIONS.TXT: Release marketing version
Version and build numbers
VERSIONS.TXT is the source of truth for MARKETING_VERSION.
Gitea's production-artifact workflow run number is the source of truth for
CURRENT_PROJECT_VERSION; .gitea/workflows/production-artifact.yml exports
it as OPENCODE_PRODUCTION_ARTIFACT_RUN_NUMBER.
scripts/artifact.sh must inject both resolved values into the built bundle.
Do not create timestamp build numbers or manually increment the committed
fallback build number for agent-driven releases.
Settings displays the bundled values as Version (Build). When diagnosing a
reported build, verify both values from the built .app or installed app.
For local physical-device builds, use the committed fallback build number
unless reproducing a specific Gitea artifact. Never label a local build with
a Gitea run number unless it was built from that run's exact source revision.
Artifacts: ./scripts/artifact.sh (simulator .app.zip by default; OPENCODE_ARTIFACT_MODE=testflight or ./scripts/artifact.sh testflight for TestFlight publication when credentials are set)
Check date: date +%Y-%m-%d
Manual Debug build (simulator), if not using scripts:
xcodebuild -scheme CodeAgentsMobile -configuration Debug -destination 'platform=iOS Simulator,name=iPhone 17' build
Manual tests (aligned with CI skips):
xcodebuild test -scheme CodeAgentsMobile -destination 'platform=iOS Simulator,name=iPhone 17 Pro,OS=26.2' -skip-testing:CodeAgentsMobileUITests -skip-testing:CodeAgentsMobileTests/DirectSSHTest -skip-testing:CodeAgentsMobileTests/SSHClaudeIntegrationTest
Scope/skip tests:
-only-testing:CodeAgentsMobileTests/ShortcutPromptBuilderTests or -skip-testing:CodeAgentsMobileTests/DirectSSHTest
Clean: xcodebuild -scheme CodeAgentsMobile clean
Focused recovery coverage to prefer when touching chat open / resume:
ChatDeferredStartupTests, ChatRecoveryTimingTests, ClaudeToOpenCodeMigrationTests, OpenCode hydration/session tests under MobileCodeTests/
Chat open / recovery (do not regress)
Local-first chat open: render persisted SwiftData messages before remote recovery / migration side effects.
On configure: loadMessages() first, then ClaudeToOpenCodeMigrationService.migrateIfNeeded (must not block showing local messages), then OpenCode hydration decision.
Defer MCP server refresh, managed rules setup, and broad CodeAgents UI media prefetch until after local messages and required active-session recovery complete — or until explicit MCP/tool UI actions, just-in-time send/action paths, or the project-scoped post-ready background queue.
Project switches must cancel deferred startup work so it cannot update stale chat state.
OpenCode only: initial recovery fetch is bounded and diffed against stored message/part hydration anchors; full-session refresh may run later in the background when more history may exist.
Do not reintroduce Claude proxy chat poll/sync/recovery on chat open (no ProxyStreamClient stream, no event replay, no ProxyEventRecovery).
Prefer OpenCode hydration over full reload on every re-entry.
Debug builds may emit [ChatRecoveryTiming] lines. Timing metadata only: runtimes, project ids, operation labels, elapsed ms, statuses, booleans, counts. Do not log prompts, message text, raw payloads, credentials, URLs, project paths, attachment paths, or file contents.
Commits: concise, imperative mood (e.g., "Fix scrolling in chat view"). Group related changes.
PRs must include: clear description, linked issues, test plan (commands + expected outcome), screenshots for UI, and note any migrations/config touches. CI via ./scripts/ci.sh (or equivalent skips above) must pass.
Proxy Deployment (legacy Claude proxy)
Local proxy repo: ~/Projects/mobilecode.swift/MobileCode/server/claude-proxy
Policy: For proxy-only work, do not push iOS app code changes. If a task is MCP/proxy/server related and does not touch MobileCode/ UI/app logic, update only server/claude-proxy and avoid committing/pushing iOS project files.
Remember: Claude Code / Claude proxy is deprecated relative to OpenCode; only change the proxy when maintaining legacy compatibility.
Commit proxy code changes to the proxy repo.
Push the proxy repo to GitHub.
SSH to the test server (if available).
Pull latest: cd /opt/claude-proxy && git pull
Proxy venv lives at /opt/claude-proxy/.venv (created by installer).
If requirements changed: cd /opt/claude-proxy && . .venv/bin/activate && pip install -r requirements.txt
Restart: supervisorctl restart claude-proxy
Firebase Functions Deployment
Deploy (from repo): cd server/firebase-functions && firebase deploy --only functions,firestore
If firebase login --reauth fails due to the localhost callback, use: firebase login --reauth --no-localhost
If the CLI prompts ? Enter authorization code: and you have a redirect URL like http://localhost:9005?...&code=XYZ&..., paste only the XYZ part.
If deploy fails with HTTP Error: 429, Quota exceeded for quota metric 'Mutate requests' from serviceusage.googleapis.com, wait 1–2 minutes and retry (Firebase CLI sometimes hits a shared API-enable rate limit). If it persists, enable the missing API(s) in Google Cloud Console (often firebaseextensions.googleapis.com) and retry.
If deploy fails at Error generating the service identity for pubsub.googleapis.com (or eventarc.googleapis.com), wait 1–2 minutes and retry. If it persists, run (in Cloud Shell): gcloud beta services identity create --project <project-id> --service pubsub.googleapis.com (and eventarc.googleapis.com), then retry deploy.
Security & Configuration Tips
Never commit secrets or API keys; use Keychain at runtime (see KeychainManager). Do not commit real GoogleService-Info.plist contents — use GoogleService-Info.plist.example as the template.
Don’t commit local build artifacts or *.xcuser*; keep .gitignore intact.
Review PRIVACY_POLICY.md and relevant docs in specs/ / docs/plans/ when changing data flows.
If using XcodeBuildMCP, use the installed XcodeBuildMCP skill before calling XcodeBuildMCP tools.
Public project page (Selfhosted Ninja)
Agent skills for this repo: .agents/skills/skills.md.
Use the wordpress-manager skill (.agents/skills/wordpress-manager/) whenever user-facing product info changes so https://selfhosted.ninja/projects/codeagents-mobile/ stays accurate (features, links, screenshots, FAQ, requirements). Note OpenCode as the supported runtime when updating public copy; Claude Code is deprecated.
Site config (selfhosted only): .skills-data/wordpress-manager/sites.yaml or the template at .agents/skills/wordpress-manager/sites.selfhosted.yaml.