treescope-cli
v0.2.0
Published
Command-line client for the Treescope runtime view inspector — built for shells, scripts, and coding agents (LLMs) to inspect a running app's UI hierarchy.
Maintainers
Readme
Treescope CLI
A command-line client for the Treescope runtime view inspector.
The Treescope runtime embeds a small loopback HTTP + WebSocket server inside your app (DEBUG builds). The browser viewer is one client of that server; this CLI is another. It lets you inspect a running app's UIKit / AppKit / SwiftUI view hierarchy from a shell, a script, or — the main motivation — a coding agent / LLM that needs to "see" the UI.
$ treescope status
Connected to Treescope server v0.1.0 (protocol 1) at 127.0.0.1:50067
App: MyApp (com.example.MyApp)
OS: iOS 18.2
Device: iPhone 16 Pro — iPhone17,1 (Simulator)
Screen: 393 x 852 @3x
Capabilities: snapshots, liveEditing, swiftUI, highlighting, pushUpdatesInstall
cd CLI
npm install
npm run build
npm link # optional: puts `treescope` on your PATHRequires Node 18+. Without npm link, run via node dist/index.js ….
Prerequisites
Your app must be running with the Treescope server started (DEBUG builds):
import TreescopeServer
#if DEBUG
Treescope.start()
#endif- Simulator / macOS: the server is reachable on
127.0.0.1directly. - Physical iOS device: forward the port over USB first:
iproxy 50067 50067 # from libimobiledevice
The CLI auto-discovers the port by scanning 50067…50082; override with --port.
Commands
| Command | Description |
| --- | --- |
| treescope status | Connect and print device info + capabilities |
| treescope screenshot | Save a PNG of the whole current screen (auto-selects the window) |
| treescope tree | Print the view hierarchy as a compact tree |
| treescope inspect <nodeID> | Show all properties for one node |
| treescope find <query> | Search nodes by name / class / label / text |
| treescope snapshot <nodeID> | Save a rendered PNG of one node |
| treescope set <nodeID> <keyPath> <value> | Live-edit an editable attribute |
| treescope mcp | Run as an MCP server (stdio) for coding agents — see below |
Global options
--host <host> server host (default 127.0.0.1)
-p, --port <n> server port (default: auto-discover from 50067)
--timeout <ms> network timeout (default 8000)
--json emit machine-readable JSON instead of formatted textscreenshot
treescope screenshot # -> screenshot.png (whole screen)
treescope screenshot -o screen.png --scale 2
treescope shot # aliasCaptures the entire current screen in one call — no node id needed. It picks the visible
window automatically and falls back to its content view, so it works on iOS (where a
UIWindow renders) and macOS (where the NSWindow root does not). This is the quickest way
for a coding agent to see the UI; re-run it after a set to confirm the change visually.
tree
treescope tree # full hierarchy
treescope tree --depth 4 # limit depth (token-friendly)
treescope tree --visible-only # hide hidden / zero-size / transparent
treescope tree --filter LoginButton # only subtrees matching a substring
treescope tree --no-swiftui # skip SwiftUI reflection
treescope tree --json # raw HierarchySnapshot JSONOutput is one compact line per node:
UIWindow · #obj:1 (0, 0, 390, 844)
└─ UIView ContainerView · #obj:2 (0, 0, 390, 844)
├─ UILabel "Welcome back" · #obj:3 (16, 60, 200, 24)
└─ UIButton "Log In" LoginButton · #obj:4 (16, 200, 358, 48) hidden α0.50#obj:… is the node ID — pass it to inspect, snapshot, or set.
inspect
treescope inspect obj:3
treescope inspect obj:3 --jsonPrints geometry plus every captured attribute, grouped by section, marking which ones are live-editable and their key path.
find
treescope find Button
treescope find "Welcome back" --limit 20 --jsonMatches display name, class name, accessibility label, and string/enum attribute values (e.g. label text). Returns node IDs and their path from the root.
snapshot
treescope snapshot obj:4 -o button.png --scale 2set
treescope set obj:4 alpha 0.5 # number inferred
treescope set obj:4 isHidden true # bool inferred
treescope set obj:3 text "Hi" -t stringOnly attributes reported as editable by inspect can be set. Value type is
inferred (bool / integer / number / string); override with --type.
Using it from a coding agent
The CLI is designed to be driven by an LLM/agent:
--jsonon every command yields stable, parseable output.tree --depth Nandtree --filter …keep responses small enough for a context window instead of dumping a 20k-node hierarchy.findlets the agent locate a node by what it knows (a label, a class) before drilling in withinspect.snapshotproduces a PNG a multimodal model can look at.
A typical agent loop: status → screenshot (look at the screen) →
tree --depth 3 → find <thing> → inspect <id> → optionally set <id> …
→ screenshot again to confirm.
There's also a ready-made Claude Code skill at
.claude/skills/treescope/ that encodes this loop —
copy it into your own project's .claude/skills/ to give agents UI sight there too.
MCP server mode
treescope mcp runs the inspector as a Model Context Protocol
server over stdio, so agents like Claude Code can call it as tools — no shell
parsing required. It exposes:
| Tool | Purpose |
| --- | --- |
| treescope_status | Device info + capabilities |
| treescope_screenshot | Whole-screen PNG returned inline (multimodal) — see the UI in one call |
| treescope_get_tree | Compact hierarchy (maxDepth, visibleOnly, hideSystem, filter, includeSwiftUI, includeLayers) |
| treescope_inspect_node | All properties for one node |
| treescope_find_nodes | Search by name / class / label / text |
| treescope_get_snapshot | Rendered PNG returned as inline image content (multimodal) |
| treescope_set_attribute | Live-edit an editable attribute |
Outputs are token-efficient: treescope_get_tree defaults to maxDepth: 4 and
supports a filter, so an agent drills down with find / inspect rather than
pulling a whole hierarchy at once.
Register with Claude Code
claude mcp add treescope -- node /absolute/path/to/treescope/CLI/dist/index.js mcpOr in an MCP client config (claude_desktop_config.json, etc.):
{
"mcpServers": {
"treescope": {
"command": "node",
"args": ["/absolute/path/to/treescope/CLI/dist/index.js", "mcp"]
}
}
}The server auto-discovers the app's port (50067…50082). To pin host/port, add
them before mcp: "args": [".../dist/index.js", "--port", "50067", "mcp"].
Testing
npm test # builds, then runs node --testThe suite is self-contained: a mock Treescope server (test/mock-server.mjs)
implements the wire protocol, and the tests exercise the formatters
(format.test.mjs), every CLI command (cli.test.mjs), and every MCP tool via a
real MCP client over stdio (mcp.test.mjs).
Roadmap
--watchmode subscribing tohierarchyChangedpush events.- Bonjour-based discovery in addition to the loopback port scan.
Protocol
The wire types in src/protocol.ts mirror the Swift
TreescopeProtocol module and the browser viewer's Web/src/protocol.ts. The
client (src/client.ts) is a Node port of Web/src/client.ts, correlating
responses to requests by envelope id over the same WebSocket protocol.
