Local-first Modus desktop agent with integrated tools for terminal, browser, codebase exploration, app launching, planning, and subagent coordination
Modus Desktop Agent demonstrates solid tool design with comprehensive descriptions and well-structured schemas. All 16 tools have clear, action-oriented names starting with verbs (launch_, browser_, terminal_, etc.) and detailed descriptions (100-300+ chars). Input schemas use TypeBox with proper type definitions and descriptions for all parameters. However, output schemas are not explicitly documented in the visible code, and error handling guidance is minimal. Tool composition is strong, each tool has a single responsibility and chains naturally (e.g., browser_tabs → browser_cdp → browser_events). Parameter constraints use enums where appropriate (browser_tabs actions, terminal_write keys). The codebase shows production-grade patterns like idempotent operations and clear risk classifications (WRITE, READ_ONLY, DESTRUCTIVE), but lacks explicit recovery guidance in error responses.
Ask the user one to four menu-style questions and wait for the answer. Use ONLY for genuine product/preference decisions that exploring the repo cannot settle — never for facts you can discover yourself. Each question offers 2–4 mutually-exclusive options (mark one recommended); the user can also type a custom answer or skip. If skipped, proceed with your recommended defaults and record them as assumptions.
Send a raw Chrome DevTools Protocol command to the Modus browser. Prefer official CDP methods directly: Page.navigate, Runtime.evaluate, DOM.querySelector, Input.dispatchMouseEvent, Network.*, Profiler.*, etc.
Drain recent raw CDP events for the target tab. Use after browser_cdp commands to inspect navigation, network, target, page, and runtime events; event names are observed here, not sent as browser_cdp commands.
Capture the target tab as an image for visual confirmation or coordinate calibration. For text, DOM, lists, and page state, prefer browser_cdp.
Capture a text accessibility snapshot of the target tab. Use it to inspect visible structure, controls, labels, and state without relying on image vision.
Output schemas not explicitly documented in visible code. While input schemas are well-defined via TypeBox, return types and response structures are not formally declared, forcing LLMs to infer output shape from descriptions alone.
Error handling lacks recovery guidance. Tool descriptions do not include actionable error messages or next steps (e.g., 'If launch_app reports exit, check the executable path and build'). Error responses should guide LLM recovery.
Destructive operations (terminal_kill, plan_write overwrite) lack confirmation or dry-run patterns. Agents should be able to preview changes or confirm before irreversible actions.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 63 | 2026-07-28+ | v2 |
List, create, close, or select Modus in-app browser tabs. Start browser work with browser_tabs({action:'list'}); create a tab if none exists.
Explore the current workspace's local codebase index before broad file reading. Use it to find relevant files, symbols, architecture entry points, and a few code snippets with far fewer tokens than grep/read exploration. It is read-only and local; read the live file before editing because the index is a navigation snapshot.
Launch a GUI desktop application as its own independent window process. Use this for programs that open a window — editors, IDEs, games, Electron/desktop apps. Modus starts it detached, captures the real OS process id, waits briefly, then verifies the window is still running and reports its pid and window title. For servers, CLIs, and watchers (no window, you want logs) use terminal_run with background:true instead. Stop a launched app later with terminal_kill using the returned id.
Write or update the implementation plan for the current Plan Mode session. The plan is a session-scoped Markdown document — the source of truth used for execution. Research the codebase (read/grep/find/ls) and resolve open questions with the user BEFORE writing, then call this once with the complete plan. After a successful call, stop; only a later user revision request should write a new version. Do not implement anything in Plan Mode.
Start a bounded child subagent with its own context window and return immediately. Collect results later with wait() in the same turn.
Kill a running terminal and its process tree. Use it to stop servers, apps, or long-running commands.
List all active terminals in the current session. Returns terminal ids, commands, status (running/exited), and pids.
Read output from a running or exited terminal. Returns the current output buffer, exit code (if exited), and cursor position. Use it to inspect long-running process output, verify readiness, or check for errors.
Run a shell command in a managed terminal that appears in the Modus terminal panel. Foreground commands wait briefly for completion, then return a terminal id while continuing to run. Long-lived processes (dev servers, apps, watchers): set background:true and pass the program DIRECTLY. Modus watches it for a short window and reports whether it stayed running (ALIVE) or exited. Add ready_when to verify it is actually serving (a port opening, a log line, or an HTTP 2xx) — not just alive. Observe running commands with terminal_read; send input with terminal_write.
Send input (text or keystrokes) to a running terminal. Use it to interact with interactive CLIs, respond to prompts, or send signals.
Create and maintain a live task list for the current coding session, shown to the user beside the conversation. Use it for multi-step work (3+ steps): write the plan once, then update item statuses as you progress. Keep exactly one item in_progress at a time, mark items completed immediately after finishing them, and cancel items that became irrelevant. Skip it for trivial single-step tasks.
Parameter constraints for numeric fields (maxLines, maxDepth, limit) lack explicit min/max bounds in descriptions. browser_snapshot.maxLines and fast_codebase.limit should document valid ranges.
task tool description is vague ('Start a bounded child subagent'). Does not explain when to use task vs inline execution, what context isolation means, or how to collect results with wait().