{ config, lib, pkgs, inputs, ... }: let # what model should be used in place of haiku? # glm 4.7-flash is an example haiku-model = "anthropic/claude-haiku-4-5"; opus-model = "anthropic/claude-opus-4-6"; ohMyOpencodeConfig = { "$schema" = "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/assets/oh-my-opencode.schema.json"; agents = { sisyphus.model = opus-model; oracle = { model = "openrouter/openai/gpt-5.2"; variant = "high"; }; librarian.model = haiku-model; explore.model = haiku-model; multimodal-looker.model = "openrouter/google/gemini-3-flash-preview"; prometheus.model = opus-model; metis.model = opus-model; momus = { model = "openrouter/openai/gpt-5.2"; variant = "medium"; }; atlas.model = opus-model; }; categories = { visual-engineering.model = "openrouter/google/gemini-3-pro"; ultrabrain = { model = "openrouter/openai/gpt-5.2-codex"; variant = "xhigh"; }; artistry = { model = "openrouter/google/gemini-3-pro"; variant = "max"; }; quick.model = haiku-model; "unspecified-low".model = opus-model; "unspecified-high".model = opus-model; writing.model = "openrouter/google/gemini-3-flash-preview"; }; }; in { home.packages = let ohMyOpencode = let orig = inputs.oh-my-opencode.packages.${pkgs.stdenv.hostPlatform.system}; fixed-node_modules = orig.node_modules.overrideAttrs (_: { outputHash = "sha256-qq/eEoxFELVdt4qaJkrw8XNZ/Ph/RJdsyIp7LteQE5A="; }); in orig.oh-my-opencode.overrideAttrs (_: { buildPhase = '' cp -r ${fixed-node_modules}/node_modules . chmod -R u+w node_modules patchShebangs node_modules/ export HOME=$(mktemp -d) bun run build ''; }); in [ ohMyOpencode pkgs.playwright-driver.browsers ]; home.sessionVariables = { PLAYWRIGHT_BROWSERS_PATH = "${pkgs.playwright-driver.browsers}"; PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD = "1"; }; xdg.configFile."opencode/oh-my-opencode.json".text = builtins.toJSON ohMyOpencodeConfig; xdg.configFile."opencode/skills/playwright.md".text = let browsers = pkgs.playwright-driver.browsers; chromiumDir = builtins.head ( builtins.filter (n: builtins.match "chromium-[0-9]+" n != null) ( builtins.attrNames browsers.passthru.entries ) ); chromiumPath = "${browsers}/${chromiumDir}/chrome-linux64/chrome"; in '' --- name: playwright description: "MUST USE for any browser-related tasks. Browser automation via Playwright MCP - verification, browsing, information gathering, web scraping, testing, screenshots, and all browser interactions." mcp: playwright: command: npx args: - "@playwright/mcp@latest" - "--executable-path" - "${chromiumPath}" - "--user-data-dir" - "${config.home.homeDirectory}/.cache/playwright-mcp" --- # Playwright Browser Automation This skill provides browser automation capabilities via the Playwright MCP server. ''; programs.opencode = { package = inputs.llm-agents.packages.${pkgs.stdenv.targetPlatform.system}.opencode; enable = true; rules = '' You are an intelligent and observant agent. If instructed to commit, disable gpg signing. You are on nixOS, if you don't have access to a tool, you can access it via the `nix-shell` command. ## Think deeply about everything. When given a problem, break it down, abstract it out, understand the fundamentals, then solve it in the real world. ## Misc For long-running commands, make sure you set the timeout of the Bash tool provided to a larger value. Do NOT read secret files. Do not directly read files that are api keys or are contextually sensitive. Do NOT run `skill_mcp [mcp_name=playwright, tool_name=browser_install]` as browsers are provided by NixOS via PLAYWRIGHT_BROWSERS_PATH. ## Behavior Do not be sycophantic in your responses. Do not use emojis unless explicitly asked to. This includes in code. Use Test Driven Development methodology. ## Nix For using `nix build` append `-L` to get better visibility into the logs. If you get an error that a file can't be found, always try to `git add` the file before trying other troubleshooting steps. ## Android UI Interaction Workflow Summary 1. Taking Screenshots adb exec-out screencap -p > /tmp/screen.png Captures the current screen state as a PNG image. 2. Analyzing Screenshots I delegate screenshot analysis to an explore agent rather than analyzing images directly: mcp_task(subagent_type="explore", prompt="Analyze /tmp/screen.png. What screen is this? What elements are visible?") The agent describes the UI, identifies elements, and estimates Y coordinates. 3. Getting Precise Element Coordinates UI Automator dump - extracts the full UI hierarchy as XML: adb shell uiautomator dump /sdcard/ui.xml && adb pull /sdcard/ui.xml /tmp/ui.xml Then grep for specific elements: # Find by text grep -oP 'text="Login".*?bounds="[^"]*"' /tmp/ui.xml # Find by class grep -oP 'class="android.widget.EditText".*?bounds="[^"]*"' /tmp/ui.xml Bounds format: [left,top][right,bottom] → tap center: ((left+right)/2, (top+bottom)/2) 4. Tapping Elements adb shell input tap X Y Where X, Y are pixel coordinates from the bounds. 5. Text Input adb shell input text "some_text" Note: Special characters need escaping (\!, \;, etc.) 6. Other Gestures # Swipe/scroll adb shell input swipe startX startY endX endY duration_ms # Key events adb shell input keyevent KEYCODE_BACK adb shell input keyevent KEYCODE_ENTER 7. WebView Limitation - UI Automator can see WebView content if accessibility is enabled - Touch events on iframe content (like Cloudflare Turnstile) often fail due to cross-origin isolation - Form fields in WebViews work if you get exact bounds from the UI dump Typical Flow 1. Take screenshot → analyze with explore agent (get rough layout) 2. Dump UI hierarchy → grep for exact element bounds - NEVER ASSUME COORDINATES. You must ALWAYS check first. - Do this before ANY tap action as elements on the screen may of changed. 3. Calculate center coordinates from bounds 4. Tap/interact 5. Wait → screenshot → verify result ''; settings = { theme = "opencode"; model = opus-model; # small model used for titles small_model = "openrouter/openai/gpt-oss-20b:free"; autoshare = false; # note: this updates opencode (and plugins like oh-my-opencode) at launch, # bypassing the version pinned in flake.lock autoupdate = true; agent = { }; plugin = [ "oh-my-opencode" ]; provider = { openrouter = { models = { "openai/gpt-oss-20b:free" = { }; "minimax/minimax-m2.1" = { }; "openai/gpt-5.2-codex" = { variant = "xhigh"; }; }; options = { # TODO! use agenix here instead apiKey = "{file:${../secrets/openrouter_api_key}}"; }; }; }; }; }; }