Saarthi is a local Hyprland desktop-automation CLI. It exposes 62 commands through a discoverable saarthi <noun> <verb> tree without loading a large tool-schema catalog into every agent session.
It can inspect and control windows/workspaces, capture screenshots, launch policy-approved apps, drive keyboard/mouse input, query accessibility and OCR, operate existing tmux panes, automate the local Zen browser, and verify UI state. It intentionally does not expose arbitrary shell execution, clipboard access, remote transport, or an MCP server.
Requirements: Node.js 22+, pnpm, Hyprland, and the external tools needed by the commands you use (hyprctl, grim, wtype, ydotool, tesseract, ImageMagick, tmux, and AT-SPI Python bindings).
pnpm install
pnpm build
npm link
saarthi --helppackage.json installs saarthi from dist/src/cli.js. Re-run pnpm build after source changes; the linked command immediately uses the rebuilt file.
Saarthi used to run as an MCP server. If you registered it with an MCP host (e.g. Claude Code), remove that registration on each machine — it is what loaded ~90 tool schemas into every session, and the CLI replaces it. Nothing in this repo can remove it for you; it lives in your host config.
- Claude Code: delete the
saarthi_mcpentry frommcpServersin~/.claude.json(or runclaude mcp remove saarthi_mcp). - Drop any
mcp__saarthi_mcp__*permission entries from.claude/settings.local.jsonand allowBash(saarthi *)instead.
After that, agents call the CLI through their shell rather than connecting to a server.
export SAARTHI_SESSION_ID="agent-$(date +%s)"
saarthi window --help
saarthi window list
saarthi window list --json | jq '.windows[] | {id, class, title}'
saarthi workspace focus 3 --json
saarthi screenshot capture --target full --jsonHuman-readable text is the default. --json emits the handler's structured payload to stdout. Errors go to stderr and operational failures use stable non-zero exit codes.
Screenshots are written to disk and return a path. An agent must read/view that path after capture; capture success is not visual inspection.
Natural primary arguments can be positional for common commands:
saarthi window focus 0xABC
saarthi grid click 14
saarthi workspace focus 3All options remain discoverable through command help:
saarthi browser open-url --helpCross-invocation state lives under ~/.local/state/saarthi/:
grid-session.json: active grid targeting sessionlaunch-timestamps.json: policy rate-limit windowoverlay-task.json: explicit overlay task lifecyclestatus.json: atomic HUD status snapshotaudit.jsonl: append-only audit events
Set SAARTHI_SESSION_ID once per agent run to group audit and trace events. Without it, each invocation gets its own generated id.
The eyes overlay is independent of the CLI and remains the only Saarthi user service:
scripts/install-overlay-service.sh
systemctl --user restart saarthi-overlay.service
systemctl --user status saarthi-overlay.service --no-pager
journalctl --user -u saarthi-overlay.service -n 100 --no-pagerDo not create a Saarthi MCP service. The CLI emits start/done status for every command and persists explicit task lifecycle commands:
saarthi overlay task-start --label "desktop task"
saarthi overlay task-ping --state waiting
saarthi overlay task-complete --status done- Window ids are validated against live Hyprland state before mutating commands.
- App launches always pass
config/policy.jsonaliases, deny rules, and the persisted rate limiter. - Mutating commands append audit events and preserve act-then-verify workflows.
- Process execution uses
execFilewith argument arrays, never interpolated shell strings. SAARTHI_DRY_RUN=1exercises mutating command paths without desktop mutations.
Hyprland 0.55+ mutations use Lua dispatcher expressions centralized in src/lib/hyprland.ts. A stdout line beginning with error: is treated as failure even if hyprctl exits zero.
pnpm test
pnpm build
pnpm smokeThe verification layers are:
- adapter tests for domain behavior
- CLI unit/dispatch tests for argv, schemas, outputs, exit codes, state, and status
- registry snapshot coverage for all 62 commands
- built-binary smoke for generated help and every read-only
--jsoncommand
pnpm smoke:screenshot is the optional live screenshot check and writes a real PNG.
See docs/ARCHITECTURE.md, docs/OPERATIONS.md, and skill/SKILL.md.