"""Dump the accessibility tree of the Cursor application. Usage: python scripts/dump_a11y_tree.py [--depth N] Requires Accessibility permissions for the running terminal. """ import argparse import sys from ApplicationServices import ( AXUIElementCreateApplication, AXUIElementCopyAttributeNames, AXUIElementCopyAttributeValue, ) from Cocoa import NSWorkspace CURSOR_BUNDLE_ID = "com.todesktop.230313mzl4w4u92" def find_cursor_pid() -> int | None: """Find the PID of the running Cursor application.""" workspace = NSWorkspace.sharedWorkspace() for app in workspace.runningApplications(): bundle = app.bundleIdentifier() or "" if bundle == CURSOR_BUNDLE_ID: return app.processIdentifier() return None def dump_element(element, depth: int = 0, max_depth: int = 5) -> None: """Recursively print an AXUIElement's attributes.""" if depth > max_depth: return indent = " " * depth names_err, attr_names = AXUIElementCopyAttributeNames(element, None) if names_err or not attr_names: return role = "" title = "" value = "" description = "" for name in attr_names: err, val = AXUIElementCopyAttributeValue(element, name, None) if err: continue if name == "AXRole": role = str(val) elif name == "AXTitle": title = str(val) if val else "" elif name == "AXValue": value_str = str(val)[:100] if val else "" value = value_str elif name == "AXDescription": description = str(val) if val else "" label = role if title: label += f' title="{title}"' if description: label += f' desc="{description}"' if value: label += f' value="{value}"' print(f"{indent}{label}") err, children = AXUIElementCopyAttributeValue(element, "AXChildren", None) if not err and children: for child in children: dump_element(child, depth + 1, max_depth) def main(): parser = argparse.ArgumentParser(description="Dump Cursor's accessibility tree") parser.add_argument("--depth", type=int, default=8, help="Max depth to traverse") args = parser.parse_args() pid = find_cursor_pid() if pid is None: print("Cursor is not running.", file=sys.stderr) sys.exit(1) print(f"Found Cursor at PID {pid}") app_element = AXUIElementCreateApplication(pid) dump_element(app_element, max_depth=args.depth) if __name__ == "__main__": main()