CodeAgents Mobile

repository·main·Indexed 40 days ago

https://github.com/eugenepyvovarov/codeagentsmobile

An iOS client for OpenCode that enables connection to Linux SSH servers and interaction with coding agents. The project includes a Firebase Push Gateway for background notifications, MCP server integration, and advanced file management. It requires iOS 17.0+, Xcode 15+, and Swift 5.9+ for development.

Tokens
33.5K
Snippets
31
Records
171
Agent score
100%

What's inside CodeAgents Mobile

  1. Overview of CodeAgents Mobile Architecture

    main

    CodeAgents is an iOS application designed to provide a mobile-optimized development experience by connecting to remote servers via SSH and integrating with the Claude Code CLI by Anthropic®.

    Technology Stack

    • Language: Swift 5.9+ (modern concurrency)
    • UI Framework: SwiftUI with @Observable macro
    • Data Persistence: SwiftData (requires iOS 17+)
    • SSH Library: SwiftNIO SSH
    • Authentication: iOS Keychain Services
    • Minimum iOS Version: iOS 17.0
    • Architecture Pattern: MVVM with Protocol-Oriented Programming

    High-Level Architecture

    The app is organized into several distinct layers:

    1. UI Layer (SwiftUI): The visual interface.
    2. ViewModels (@Observable): Manages state and logic for the UI.
    3. Services Layer: Contains core logic for SSHService, ClaudeService, FileService, and AuthService.
    4. Data Layer (SwiftData): Handles persistence of projects, servers, messages, and keys.
    5. Security Layer (Keychain Services): Manages sensitive credentials.
  2. Current Limitations and Roadmap

    main

    Current Limitations

    • Terminal: Placeholder implementation only.
    • File Editing: Currently view-only; no editing capability.
    • Syntax Highlighting: Not yet implemented.
    • Host Key Validation: Currently accepts all hosts.
    • Offline Mode: Requires an active connection.

    Future Roadmap

    • Phase 1 (Terminal): Full PTY support with SwiftNIO, ANSI escape sequence processing, and command history.
    • Phase 2 (Code Editor): Syntax highlighting, code completion, and multi-file editing.
    • Phase 3 (Advanced): Biometric authentication, offline mode with sync, and iPad multi-window support.
    • Phase 4 (Collaboration): Shared sessions and real-time collaboration.
  3. Configure notification previews and privacy

    main

    The Push Gateway manages how notifications appear on the iOS lock screen to ensure privacy and clean formatting.

    Privacy Defaults

    By default, the lock-screen body is set to Reply ready and the title is the agent display name (or CodeAgents). Path fragments from the cwd are never used as the title.

    Enabling Previews

    To show a sanitized text preview in the notification:

    1. Set include_preview: true in the request.
    2. Provide a message_preview string.

    If include_preview is not set to true, any preview field provided in the request is ignored.

    Preview Sanitization

    When triggerReplyFinished is called with a message_preview, the gateway performs the following:

    • Removes fenced codeagents-ui and codeagents_ui blocks.
    • Performs whitespace normalization and truncation.
    • Uses the sanitized text for both the FCM notification body and the APNs alert body.
    • If sanitization results in no readable text, the gateway falls back to the safe default (Reply ready) instead of exposing raw UI JSON.
  4. Verify Build Metadata and Versioning Sources

    main

    The project uses several sources of truth for versioning depending on the build context:

    • Marketing Version: Defined in VERSIONS.TXT.
    • Production Artifacts (Gitea): The Gitea run number is exported as OPENCODE_PRODUCTION_ARTIFACT_RUN_NUMBER. The scripts/artifact.sh script resolves this value.
    • TestFlight: Uses the resolved values passed to asc publish.
    • Simulator Artifacts: Uses resolved values passed to XcodeBuildMCP as MARKETING_VERSION and CURRENT_PROJECT_VERSION overrides to ensure metadata consistency.
    • Local Xcode Builds: Use the committed fallback build number. Do not use timestamp counters for local builds.
  5. How Skill Marketplace Full Folder Downloads work

    main

    The Skill Marketplace supports downloading entire skill folders from marketplaces or GitHub folder URLs, rather than just a single SKILL.md file. This allows for preserving complex directory structures within a skill.

    The process follows these steps:

    1. Resolution: The SkillMarketplaceService resolves the plugin path.
    2. Download: The service uses the GitHub git/trees?recursive=1 API to enumerate all files. It filters out specific metadata folders and downloads each file via raw.githubusercontent.com into a temporary staging directory.
    3. Installation: SkillLibraryService.installSkill(from:) validates the presence of SKILL.md, parses the front matter, resolves any slug conflicts, and moves the staged folder into the global library root.
    4. Agent Sync: Once in the global library, skills can be added to an agent, which copies the full folder to #{project.path}/.claude/skills/<slug> via AgentSkillSyncService.
  6. Implement the Recurring Author Support Prompt logic

    main

    The Recurring Author Support Prompt is a non-blocking SwiftUI sheet designed to invite users to follow the author on X or provide open-source sponsorship.

    Eligibility Logic

    The prompt is eligible to appear only when:

    1. The permanent opt-out flag is false.
    2. AND either no previous presentation date exists OR at least 14 days have elapsed since the last presentation.

    Persistence and Scheduling

    Use UserDefaults to manage a schedule store containing:

    • The most recent presentation date.
    • A permanent opt-out Boolean.

    Important: Record the presentation date at the moment the sheet is presented, not when it is dismissed. This prevents users from bypassing the 14-day window via crashes or force quits. Re-check eligibility when the root view first appears and whenever the scene becomes active.

  7. Proxy Agent Identity and Portability

    main

    To ensure that environment variables and tasks remain portable across app reinstalls, the system uses a stable proxyAgentId stored within the agent's local directory.

    Identity Storage

    • Path: <agent cwd>/.claude/codeagents.json
    • Format: {"schema_version":1,"agent_id":"<uuid>"}

    Resolution Rules

    • New Agents: If the file is missing, the app creates it using the current local agent UUID.
    • Reinstalls/Sync: If the file exists but the agent_id does not match the local UUID, the server agent_id wins. The app adopts the server's ID as the proxyAgentId for all subsequent proxy calls.
  8. How Agent Skills and MCP are copied during duplication

    main

    The duplication process handles specialized configurations as follows:

    Skills Copying

    For every AgentSkillAssignment associated with the source projectId:

    1. A new assignment is inserted for the clone's ID.
    2. AgentSkillSyncService.installSkill is called for the new clone path.
    3. If a skill is missing from the global library, the process continues but returns a warning.

    MCP (Model Context Protocol) Copying

    1. The service fetches project-scoped servers using fetchServers(for: source, scope: .project).
    2. The managed scheduler server is explicitly skipped.
    3. Project-scoped servers are added to the clone using addServer(..., scope: .project, for: clone).
    4. Note: Global MCP servers are not re-copied as they are already shared on the server.
  9. Debug chat performance with [ChatRecoveryTiming]

    main

    Debug builds emit lightweight timing logs prefixed with [ChatRecoveryTiming] to help diagnose chat reopening and resumption performance. These logs help distinguish the costs of:

    • Local SwiftData load
    • Claude→OpenCode migration
    • OpenCode hydration
    • Persistence saves
    • Media prefetch
    • MCP fetch
    • Resume-streaming

    Note: Timing metadata is strictly limited to runtime names, operation labels, and counts. Do not include sensitive data like prompts, message text, or credentials in these logs.

  10. How Chat Link Highlighting works in MarkdownTextView

    main

    The MarkdownTextView component handles link rendering by performing a two-step process: first parsing Markdown, then detecting web URLs within the resulting text.

    • Explicit Markdown links: Preserve their original destinations.
    • Bare URLs: Plain http:// and https:// URLs that are not part of explicit Markdown syntax are automatically detected and assigned link attributes.
    • Visual Styling: All detected links are styled with the app's accent color and a single underline, applied after the base bubble color. This ensures links are distinguishable in both assistant and user message bubbles.
    • URL Handling: The view uses a scoped OpenURLAction. Taps on http or https URLs are passed to UIApplication.shared.open, delegating to the system's external URL handler. Other URL schemes follow standard SwiftUI behavior.
  11. Manage pending runs and task cleanup in the Daemon

    main

    The daemon manages pending runs to handle transient contention. To prevent replaying obsolete work, the store follows these rules:

    • Keep at most one pending run per scheduled task.
    • Discard pending entries older than one hour.
    • Discard entries for missing tasks or disabled tasks.
    • Perform a cleanup run during scheduler startup before jobs are restored to remove stale backlogs.
  12. How the TestFlight version preflight gate works

    main

    The TestFlight release process includes a preflight check in scripts/artifact.sh designed to prevent automation from wasting resources on builds that App Store Connect would reject.

    The Logic:

    1. The script authenticates with App Store Connect and lists all iOS App Store versions.
    2. It compares the version specified in VERSIONS.TXT against the highest approved or closed version found on App Store Connect using numeric version components.
    3. Pass Condition: The release proceeds only if the version in VERSIONS.TXT is strictly greater than the highest approved/closed version.
    4. Fail Condition: If the version is equal to or lower than a closed release train, the build fails immediately. Invalid version syntax or malformed App Store responses also cause a failure.

    Exemptions:

    • Editable draft versions do not block builds because their release trains are not yet closed.
    • macOS and other non-iOS platforms are ignored by this specific check.

    Timing: The gate runs before host cache preparation, package resolution, keychain unlocking, or Xcode archiving.