@mseep/clawdcursor 1.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (354) hide show
  1. package/CHANGELOG.md +2264 -0
  2. package/LICENSE +21 -0
  3. package/README.md +385 -0
  4. package/SECURITY.md +44 -0
  5. package/SKILL.md +503 -0
  6. package/dist/core/agent-loop/agent.d.ts +42 -0
  7. package/dist/core/agent-loop/agent.js +1023 -0
  8. package/dist/core/agent-loop/agent.js.map +1 -0
  9. package/dist/core/agent-loop/batch-tool.d.ts +25 -0
  10. package/dist/core/agent-loop/batch-tool.js +218 -0
  11. package/dist/core/agent-loop/batch-tool.js.map +1 -0
  12. package/dist/core/agent-loop/coord-scale.d.ts +72 -0
  13. package/dist/core/agent-loop/coord-scale.js +89 -0
  14. package/dist/core/agent-loop/coord-scale.js.map +1 -0
  15. package/dist/core/agent-loop/focus-guard.d.ts +24 -0
  16. package/dist/core/agent-loop/focus-guard.js +29 -0
  17. package/dist/core/agent-loop/focus-guard.js.map +1 -0
  18. package/dist/core/agent-loop/project-mcp.d.ts +97 -0
  19. package/dist/core/agent-loop/project-mcp.js +253 -0
  20. package/dist/core/agent-loop/project-mcp.js.map +1 -0
  21. package/dist/core/agent-loop/prompt.d.ts +45 -0
  22. package/dist/core/agent-loop/prompt.js +426 -0
  23. package/dist/core/agent-loop/prompt.js.map +1 -0
  24. package/dist/core/agent-loop/tool-meta.d.ts +93 -0
  25. package/dist/core/agent-loop/tool-meta.js +651 -0
  26. package/dist/core/agent-loop/tool-meta.js.map +1 -0
  27. package/dist/core/agent-loop/tools.d.ts +38 -0
  28. package/dist/core/agent-loop/tools.js +2134 -0
  29. package/dist/core/agent-loop/tools.js.map +1 -0
  30. package/dist/core/agent-loop/types.d.ts +170 -0
  31. package/dist/core/agent-loop/types.js +12 -0
  32. package/dist/core/agent-loop/types.js.map +1 -0
  33. package/dist/core/agent.d.ts +51 -0
  34. package/dist/core/agent.js +245 -0
  35. package/dist/core/agent.js.map +1 -0
  36. package/dist/core/app-categories.d.ts +67 -0
  37. package/dist/core/app-categories.js +108 -0
  38. package/dist/core/app-categories.js.map +1 -0
  39. package/dist/core/banner.d.ts +70 -0
  40. package/dist/core/banner.js +245 -0
  41. package/dist/core/banner.js.map +1 -0
  42. package/dist/core/classify/capability.d.ts +45 -0
  43. package/dist/core/classify/capability.js +78 -0
  44. package/dist/core/classify/capability.js.map +1 -0
  45. package/dist/core/decompose/llm-decomposer.d.ts +35 -0
  46. package/dist/core/decompose/llm-decomposer.js +156 -0
  47. package/dist/core/decompose/llm-decomposer.js.map +1 -0
  48. package/dist/core/decompose/parser.d.ts +27 -0
  49. package/dist/core/decompose/parser.js +101 -0
  50. package/dist/core/decompose/parser.js.map +1 -0
  51. package/dist/core/observability/correlation.d.ts +19 -0
  52. package/dist/core/observability/correlation.js +36 -0
  53. package/dist/core/observability/correlation.js.map +1 -0
  54. package/dist/core/observability/cost-meter.d.ts +51 -0
  55. package/dist/core/observability/cost-meter.js +134 -0
  56. package/dist/core/observability/cost-meter.js.map +1 -0
  57. package/dist/core/observability/logger.d.ts +61 -0
  58. package/dist/core/observability/logger.js +550 -0
  59. package/dist/core/observability/logger.js.map +1 -0
  60. package/dist/core/router/aliases.d.ts +50 -0
  61. package/dist/core/router/aliases.js +104 -0
  62. package/dist/core/router/aliases.js.map +1 -0
  63. package/dist/core/router/normalize.d.ts +41 -0
  64. package/dist/core/router/normalize.js +80 -0
  65. package/dist/core/router/normalize.js.map +1 -0
  66. package/dist/core/safety.d.ts +126 -0
  67. package/dist/core/safety.js +568 -0
  68. package/dist/core/safety.js.map +1 -0
  69. package/dist/core/sense/a11y-resolver.d.ts +73 -0
  70. package/dist/core/sense/a11y-resolver.js +76 -0
  71. package/dist/core/sense/a11y-resolver.js.map +1 -0
  72. package/dist/core/sense/fingerprint.d.ts +41 -0
  73. package/dist/core/sense/fingerprint.js +123 -0
  74. package/dist/core/sense/fingerprint.js.map +1 -0
  75. package/dist/core/sense/rank.d.ts +70 -0
  76. package/dist/core/sense/rank.js +192 -0
  77. package/dist/core/sense/rank.js.map +1 -0
  78. package/dist/core/sense/reactive-check.d.ts +40 -0
  79. package/dist/core/sense/reactive-check.js +48 -0
  80. package/dist/core/sense/reactive-check.js.map +1 -0
  81. package/dist/core/sense/snapshot.d.ts +19 -0
  82. package/dist/core/sense/snapshot.js +100 -0
  83. package/dist/core/sense/snapshot.js.map +1 -0
  84. package/dist/core/sense/types.d.ts +66 -0
  85. package/dist/core/sense/types.js +9 -0
  86. package/dist/core/sense/types.js.map +1 -0
  87. package/dist/core/sense/ui-map-anchors.d.ts +7 -0
  88. package/dist/core/sense/ui-map-anchors.js +24 -0
  89. package/dist/core/sense/ui-map-anchors.js.map +1 -0
  90. package/dist/core/sense/ui-map-elements.d.ts +5 -0
  91. package/dist/core/sense/ui-map-elements.js +33 -0
  92. package/dist/core/sense/ui-map-elements.js.map +1 -0
  93. package/dist/core/sense/ui-map-find.d.ts +56 -0
  94. package/dist/core/sense/ui-map-find.js +153 -0
  95. package/dist/core/sense/ui-map-find.js.map +1 -0
  96. package/dist/core/sense/ui-map-fuse.d.ts +4 -0
  97. package/dist/core/sense/ui-map-fuse.js +44 -0
  98. package/dist/core/sense/ui-map-fuse.js.map +1 -0
  99. package/dist/core/sense/ui-map-geom.d.ts +3 -0
  100. package/dist/core/sense/ui-map-geom.js +16 -0
  101. package/dist/core/sense/ui-map-geom.js.map +1 -0
  102. package/dist/core/sense/ui-map-holder.d.ts +58 -0
  103. package/dist/core/sense/ui-map-holder.js +87 -0
  104. package/dist/core/sense/ui-map-holder.js.map +1 -0
  105. package/dist/core/sense/ui-map-normalize.d.ts +19 -0
  106. package/dist/core/sense/ui-map-normalize.js +65 -0
  107. package/dist/core/sense/ui-map-normalize.js.map +1 -0
  108. package/dist/core/sense/ui-map-render.d.ts +4 -0
  109. package/dist/core/sense/ui-map-render.js +34 -0
  110. package/dist/core/sense/ui-map-render.js.map +1 -0
  111. package/dist/core/sense/ui-map-resolve.d.ts +41 -0
  112. package/dist/core/sense/ui-map-resolve.js +59 -0
  113. package/dist/core/sense/ui-map-resolve.js.map +1 -0
  114. package/dist/core/sense/ui-map-types.d.ts +66 -0
  115. package/dist/core/sense/ui-map-types.js +11 -0
  116. package/dist/core/sense/ui-map-types.js.map +1 -0
  117. package/dist/core/sense/ui-map.d.ts +29 -0
  118. package/dist/core/sense/ui-map.js +113 -0
  119. package/dist/core/sense/ui-map.js.map +1 -0
  120. package/dist/core/verify/assertions.d.ts +132 -0
  121. package/dist/core/verify/assertions.js +284 -0
  122. package/dist/core/verify/assertions.js.map +1 -0
  123. package/dist/index.d.ts +21 -0
  124. package/dist/index.js +24 -0
  125. package/dist/index.js.map +1 -0
  126. package/dist/llm/browser-config.d.ts +36 -0
  127. package/dist/llm/browser-config.js +83 -0
  128. package/dist/llm/browser-config.js.map +1 -0
  129. package/dist/llm/client.d.ts +268 -0
  130. package/dist/llm/client.js +1094 -0
  131. package/dist/llm/client.js.map +1 -0
  132. package/dist/llm/config.d.ts +79 -0
  133. package/dist/llm/config.js +375 -0
  134. package/dist/llm/config.js.map +1 -0
  135. package/dist/llm/credentials.d.ts +35 -0
  136. package/dist/llm/credentials.js +491 -0
  137. package/dist/llm/credentials.js.map +1 -0
  138. package/dist/llm/external-creds.d.ts +42 -0
  139. package/dist/llm/external-creds.js +169 -0
  140. package/dist/llm/external-creds.js.map +1 -0
  141. package/dist/llm/providers.d.ts +123 -0
  142. package/dist/llm/providers.js +717 -0
  143. package/dist/llm/providers.js.map +1 -0
  144. package/dist/paths.d.ts +31 -0
  145. package/dist/paths.js +147 -0
  146. package/dist/paths.js.map +1 -0
  147. package/dist/platform/accessibility.d.ts +139 -0
  148. package/dist/platform/accessibility.js +670 -0
  149. package/dist/platform/accessibility.js.map +1 -0
  150. package/dist/platform/cdp-driver.d.ts +318 -0
  151. package/dist/platform/cdp-driver.js +1179 -0
  152. package/dist/platform/cdp-driver.js.map +1 -0
  153. package/dist/platform/index.d.ts +11 -0
  154. package/dist/platform/index.js +69 -0
  155. package/dist/platform/index.js.map +1 -0
  156. package/dist/platform/keys.d.ts +17 -0
  157. package/dist/platform/keys.js +129 -0
  158. package/dist/platform/keys.js.map +1 -0
  159. package/dist/platform/launch-poll.d.ts +101 -0
  160. package/dist/platform/launch-poll.js +177 -0
  161. package/dist/platform/launch-poll.js.map +1 -0
  162. package/dist/platform/linux.d.ts +173 -0
  163. package/dist/platform/linux.js +1253 -0
  164. package/dist/platform/linux.js.map +1 -0
  165. package/dist/platform/macos.d.ts +136 -0
  166. package/dist/platform/macos.js +976 -0
  167. package/dist/platform/macos.js.map +1 -0
  168. package/dist/platform/native-desktop.d.ts +145 -0
  169. package/dist/platform/native-desktop.js +936 -0
  170. package/dist/platform/native-desktop.js.map +1 -0
  171. package/dist/platform/native-helper.d.ts +130 -0
  172. package/dist/platform/native-helper.js +592 -0
  173. package/dist/platform/native-helper.js.map +1 -0
  174. package/dist/platform/ocr-engine.d.ts +78 -0
  175. package/dist/platform/ocr-engine.js +363 -0
  176. package/dist/platform/ocr-engine.js.map +1 -0
  177. package/dist/platform/ps-runner.d.ts +28 -0
  178. package/dist/platform/ps-runner.js +228 -0
  179. package/dist/platform/ps-runner.js.map +1 -0
  180. package/dist/platform/types.d.ts +397 -0
  181. package/dist/platform/types.js +15 -0
  182. package/dist/platform/types.js.map +1 -0
  183. package/dist/platform/uri-handler.d.ts +75 -0
  184. package/dist/platform/uri-handler.js +273 -0
  185. package/dist/platform/uri-handler.js.map +1 -0
  186. package/dist/platform/wayland-backend.d.ts +53 -0
  187. package/dist/platform/wayland-backend.js +348 -0
  188. package/dist/platform/wayland-backend.js.map +1 -0
  189. package/dist/platform/windows.d.ts +232 -0
  190. package/dist/platform/windows.js +1210 -0
  191. package/dist/platform/windows.js.map +1 -0
  192. package/dist/postbuild.d.ts +10 -0
  193. package/dist/postbuild.js +98 -0
  194. package/dist/postbuild.js.map +1 -0
  195. package/dist/schema/snapshot.d.ts +33 -0
  196. package/dist/schema/snapshot.js +90 -0
  197. package/dist/schema/snapshot.js.map +1 -0
  198. package/dist/shortcuts.d.ts +30 -0
  199. package/dist/shortcuts.js +261 -0
  200. package/dist/shortcuts.js.map +1 -0
  201. package/dist/surface/cli.d.ts +7 -0
  202. package/dist/surface/cli.js +1556 -0
  203. package/dist/surface/cli.js.map +1 -0
  204. package/dist/surface/dashboard.d.ts +8 -0
  205. package/dist/surface/dashboard.js +1193 -0
  206. package/dist/surface/dashboard.js.map +1 -0
  207. package/dist/surface/doctor.d.ts +29 -0
  208. package/dist/surface/doctor.js +1514 -0
  209. package/dist/surface/doctor.js.map +1 -0
  210. package/dist/surface/format.d.ts +10 -0
  211. package/dist/surface/format.js +37 -0
  212. package/dist/surface/format.js.map +1 -0
  213. package/dist/surface/http-utility.d.ts +65 -0
  214. package/dist/surface/http-utility.js +336 -0
  215. package/dist/surface/http-utility.js.map +1 -0
  216. package/dist/surface/mcp-server.d.ts +91 -0
  217. package/dist/surface/mcp-server.js +280 -0
  218. package/dist/surface/mcp-server.js.map +1 -0
  219. package/dist/surface/onboarding.d.ts +15 -0
  220. package/dist/surface/onboarding.js +184 -0
  221. package/dist/surface/onboarding.js.map +1 -0
  222. package/dist/surface/pidfile.d.ts +79 -0
  223. package/dist/surface/pidfile.js +263 -0
  224. package/dist/surface/pidfile.js.map +1 -0
  225. package/dist/surface/readiness.d.ts +45 -0
  226. package/dist/surface/readiness.js +230 -0
  227. package/dist/surface/readiness.js.map +1 -0
  228. package/dist/surface/report.d.ts +68 -0
  229. package/dist/surface/report.js +341 -0
  230. package/dist/surface/report.js.map +1 -0
  231. package/dist/surface/skill-register.d.ts +14 -0
  232. package/dist/surface/skill-register.js +150 -0
  233. package/dist/surface/skill-register.js.map +1 -0
  234. package/dist/surface/version.d.ts +6 -0
  235. package/dist/surface/version.js +27 -0
  236. package/dist/surface/version.js.map +1 -0
  237. package/dist/tools/a11y.d.ts +8 -0
  238. package/dist/tools/a11y.js +545 -0
  239. package/dist/tools/a11y.js.map +1 -0
  240. package/dist/tools/a11y_depth.d.ts +19 -0
  241. package/dist/tools/a11y_depth.js +455 -0
  242. package/dist/tools/a11y_depth.js.map +1 -0
  243. package/dist/tools/agent.d.ts +15 -0
  244. package/dist/tools/agent.js +248 -0
  245. package/dist/tools/agent.js.map +1 -0
  246. package/dist/tools/batch.d.ts +46 -0
  247. package/dist/tools/batch.js +230 -0
  248. package/dist/tools/batch.js.map +1 -0
  249. package/dist/tools/cdp.d.ts +8 -0
  250. package/dist/tools/cdp.js +233 -0
  251. package/dist/tools/cdp.js.map +1 -0
  252. package/dist/tools/compact.d.ts +63 -0
  253. package/dist/tools/compact.js +418 -0
  254. package/dist/tools/compact.js.map +1 -0
  255. package/dist/tools/cost-class.d.ts +38 -0
  256. package/dist/tools/cost-class.js +117 -0
  257. package/dist/tools/cost-class.js.map +1 -0
  258. package/dist/tools/desktop.d.ts +9 -0
  259. package/dist/tools/desktop.js +346 -0
  260. package/dist/tools/desktop.js.map +1 -0
  261. package/dist/tools/electron_bridge.d.ts +41 -0
  262. package/dist/tools/electron_bridge.js +261 -0
  263. package/dist/tools/electron_bridge.js.map +1 -0
  264. package/dist/tools/extras.d.ts +22 -0
  265. package/dist/tools/extras.js +942 -0
  266. package/dist/tools/extras.js.map +1 -0
  267. package/dist/tools/favorites.d.ts +13 -0
  268. package/dist/tools/favorites.js +137 -0
  269. package/dist/tools/favorites.js.map +1 -0
  270. package/dist/tools/introspection.d.ts +13 -0
  271. package/dist/tools/introspection.js +55 -0
  272. package/dist/tools/introspection.js.map +1 -0
  273. package/dist/tools/ocr.d.ts +8 -0
  274. package/dist/tools/ocr.js +66 -0
  275. package/dist/tools/ocr.js.map +1 -0
  276. package/dist/tools/orchestration.d.ts +7 -0
  277. package/dist/tools/orchestration.js +377 -0
  278. package/dist/tools/orchestration.js.map +1 -0
  279. package/dist/tools/playbooks/extract-compose.d.ts +22 -0
  280. package/dist/tools/playbooks/extract-compose.js +85 -0
  281. package/dist/tools/playbooks/extract-compose.js.map +1 -0
  282. package/dist/tools/playbooks/find-replace.d.ts +11 -0
  283. package/dist/tools/playbooks/find-replace.js +56 -0
  284. package/dist/tools/playbooks/find-replace.js.map +1 -0
  285. package/dist/tools/playbooks/index.d.ts +63 -0
  286. package/dist/tools/playbooks/index.js +70 -0
  287. package/dist/tools/playbooks/index.js.map +1 -0
  288. package/dist/tools/playbooks/keys-blocklist.d.ts +24 -0
  289. package/dist/tools/playbooks/keys-blocklist.js +89 -0
  290. package/dist/tools/playbooks/keys-blocklist.js.map +1 -0
  291. package/dist/tools/registry.d.ts +40 -0
  292. package/dist/tools/registry.js +560 -0
  293. package/dist/tools/registry.js.map +1 -0
  294. package/dist/tools/safety-gate.d.ts +16 -0
  295. package/dist/tools/safety-gate.js +70 -0
  296. package/dist/tools/safety-gate.js.map +1 -0
  297. package/dist/tools/scheduler.d.ts +76 -0
  298. package/dist/tools/scheduler.js +413 -0
  299. package/dist/tools/scheduler.js.map +1 -0
  300. package/dist/tools/shortcuts.d.ts +13 -0
  301. package/dist/tools/shortcuts.js +205 -0
  302. package/dist/tools/shortcuts.js.map +1 -0
  303. package/dist/tools/smart.d.ts +15 -0
  304. package/dist/tools/smart.js +785 -0
  305. package/dist/tools/smart.js.map +1 -0
  306. package/dist/tools/types.d.ts +174 -0
  307. package/dist/tools/types.js +67 -0
  308. package/dist/tools/types.js.map +1 -0
  309. package/dist/tools/window-text.d.ts +15 -0
  310. package/dist/tools/window-text.js +39 -0
  311. package/dist/tools/window-text.js.map +1 -0
  312. package/dist/types.d.ts +122 -0
  313. package/dist/types.js +41 -0
  314. package/dist/types.js.map +1 -0
  315. package/native/Package.swift +38 -0
  316. package/native/README.md +113 -0
  317. package/native/Sources/ClawdCursorHelper/main.swift +602 -0
  318. package/native/Sources/ClawdCursorHost/main.swift +182 -0
  319. package/native/Sources/PermissionCheck/main.swift +53 -0
  320. package/native/Sources/ScreenshotHelper/main.swift +219 -0
  321. package/native/build.sh +139 -0
  322. package/native/entitlements.plist +12 -0
  323. package/package.json +115 -0
  324. package/scripts/banner.ps1 +112 -0
  325. package/scripts/coord-accuracy.ps1 +140 -0
  326. package/scripts/coord-uwp.ps1 +80 -0
  327. package/scripts/edge-glow.ps1 +180 -0
  328. package/scripts/find-element.ps1 +198 -0
  329. package/scripts/get-foreground-window.ps1 +71 -0
  330. package/scripts/get-screen-context.ps1 +183 -0
  331. package/scripts/get-windows.ps1 +66 -0
  332. package/scripts/install-panic-hotkey.ps1 +46 -0
  333. package/scripts/interact-element.ps1 +431 -0
  334. package/scripts/invoke-element.ps1 +314 -0
  335. package/scripts/linux/atspi-bridge.py +356 -0
  336. package/scripts/linux/ocr-recognize.py +154 -0
  337. package/scripts/mac/_window-picker.jxa +163 -0
  338. package/scripts/mac/find-element.jxa +0 -0
  339. package/scripts/mac/find-element.sh +161 -0
  340. package/scripts/mac/focus-window.jxa +284 -0
  341. package/scripts/mac/get-focused-element.jxa +102 -0
  342. package/scripts/mac/get-foreground-window.jxa +173 -0
  343. package/scripts/mac/get-screen-context.jxa +197 -0
  344. package/scripts/mac/get-ui-tree.sh +141 -0
  345. package/scripts/mac/get-windows.jxa +117 -0
  346. package/scripts/mac/interact-element.sh +235 -0
  347. package/scripts/mac/invoke-element.jxa +408 -0
  348. package/scripts/mac/ocr-recognize.swift +124 -0
  349. package/scripts/ocr-recognize.ps1 +102 -0
  350. package/scripts/postinstall-native.js +48 -0
  351. package/scripts/ps-bridge.ps1 +830 -0
  352. package/scripts/smoke-mcp.ps1 +119 -0
  353. package/scripts/sync-version.ts +178 -0
  354. package/scripts/verify-install.js +81 -0
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Linux OCR via Tesseract (pytesseract) or tesseract CLI.
4
+ Takes an image path, outputs JSON result to stdout.
5
+ Matches the same JSON format as ocr-recognize.ps1 (Windows).
6
+
7
+ Usage: python3 ocr-recognize.py /path/to/image.png
8
+
9
+ Requires: tesseract-ocr package
10
+ Ubuntu/Debian: sudo apt install tesseract-ocr
11
+ Fedora: sudo dnf install tesseract
12
+ Arch: sudo pacman -S tesseract
13
+
14
+ Optional: pip install pytesseract (for bounding boxes)
15
+ """
16
+
17
+ import json
18
+ import subprocess
19
+ import sys
20
+ import os
21
+ import shutil
22
+
23
+ def ocr_with_tesseract_cli(image_path):
24
+ """Use tesseract CLI with TSV output for bounding boxes."""
25
+ try:
26
+ result = subprocess.run(
27
+ ['tesseract', image_path, '-', 'tsv'],
28
+ capture_output=True, text=True, timeout=30
29
+ )
30
+ if result.returncode != 0:
31
+ return {"error": f"tesseract failed: {result.stderr.strip()}"}
32
+
33
+ elements = []
34
+ lines_text = []
35
+ current_line = -1
36
+
37
+ for line in result.stdout.strip().split('\n')[1:]: # skip header
38
+ parts = line.split('\t')
39
+ if len(parts) < 12:
40
+ continue
41
+
42
+ level, page, block, par, line_num, word_num = parts[:6]
43
+ left, top, width, height = parts[6:10]
44
+ conf = parts[10]
45
+ text = parts[11].strip() if len(parts) > 11 else ''
46
+
47
+ if not text or conf == '-1':
48
+ continue
49
+
50
+ line_idx = int(line_num)
51
+ if line_idx != current_line:
52
+ current_line = line_idx
53
+ if text:
54
+ lines_text.append(text)
55
+ else:
56
+ lines_text.append('')
57
+ else:
58
+ if lines_text:
59
+ lines_text[-1] += ' ' + text
60
+
61
+ elements.append({
62
+ "text": text,
63
+ "x": int(left),
64
+ "y": int(top),
65
+ "width": int(width),
66
+ "height": int(height),
67
+ "confidence": round(max(0, int(conf)) / 100, 2),
68
+ "line": line_idx
69
+ })
70
+
71
+ return {
72
+ "elements": elements,
73
+ "fullText": '\n'.join(lines_text)
74
+ }
75
+ except FileNotFoundError:
76
+ return {"error": "tesseract not found. Install: sudo apt install tesseract-ocr"}
77
+ except subprocess.TimeoutExpired:
78
+ return {"error": "tesseract timed out after 30s"}
79
+ except Exception as e:
80
+ return {"error": f"tesseract error: {str(e)}"}
81
+
82
+
83
+ def ocr_with_pytesseract(image_path):
84
+ """Use pytesseract for bounding boxes (if installed)."""
85
+ try:
86
+ import pytesseract
87
+ from PIL import Image
88
+
89
+ img = Image.open(image_path)
90
+ data = pytesseract.image_to_data(img, output_type=pytesseract.Output.DICT)
91
+
92
+ elements = []
93
+ lines_text = []
94
+ current_line = -1
95
+
96
+ for i in range(len(data['text'])):
97
+ text = data['text'][i].strip()
98
+ conf = int(data['conf'][i])
99
+
100
+ if not text or conf < 0:
101
+ continue
102
+
103
+ line_idx = data['line_num'][i]
104
+ if line_idx != current_line:
105
+ current_line = line_idx
106
+ lines_text.append(text)
107
+ else:
108
+ if lines_text:
109
+ lines_text[-1] += ' ' + text
110
+
111
+ elements.append({
112
+ "text": text,
113
+ "x": data['left'][i],
114
+ "y": data['top'][i],
115
+ "width": data['width'][i],
116
+ "height": data['height'][i],
117
+ "confidence": round(conf / 100, 2),
118
+ "line": line_idx
119
+ })
120
+
121
+ return {
122
+ "elements": elements,
123
+ "fullText": '\n'.join(lines_text)
124
+ }
125
+ except ImportError:
126
+ return None # Fall back to CLI
127
+ except Exception as e:
128
+ return {"error": f"pytesseract error: {str(e)}"}
129
+
130
+
131
+ def main():
132
+ if len(sys.argv) < 2:
133
+ print(json.dumps({"error": "Usage: ocr-recognize.py <image-path>"}))
134
+ return
135
+
136
+ image_path = sys.argv[1]
137
+ if not os.path.isfile(image_path):
138
+ print(json.dumps({"error": f"Image not found: {image_path}"}))
139
+ return
140
+
141
+ # Try pytesseract first (better bounding boxes), fall back to CLI
142
+ result = ocr_with_pytesseract(image_path)
143
+ if result is None:
144
+ # pytesseract not installed, use CLI
145
+ if shutil.which('tesseract'):
146
+ result = ocr_with_tesseract_cli(image_path)
147
+ else:
148
+ result = {"error": "No OCR available. Install: sudo apt install tesseract-ocr"}
149
+
150
+ print(json.dumps(result))
151
+
152
+
153
+ if __name__ == '__main__':
154
+ main()
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env osascript -l JavaScript
2
+
3
+ /**
4
+ * _window-picker.jxa
5
+ *
6
+ * Shared reference implementation for "which window of a process is the
7
+ * user's real interaction window?". osascript cannot `require()` other
8
+ * scripts, so the helpers below are also INLINED into the consumer scripts.
9
+ * Treat this file as the source of truth — if you change scoring here, mirror
10
+ * the change in:
11
+ * - scripts/mac/focus-window.jxa
12
+ * - scripts/mac/get-foreground-window.jxa
13
+ *
14
+ * Intended consumers:
15
+ * - focus-window.jxa (uses findTitleMatchWindow + pickInteractionWindow fallback)
16
+ * - get-foreground-window.jxa (uses pickInteractionWindow)
17
+ *
18
+ * Heuristics, in order of weight:
19
+ * 1. Visible area (largest wins; rejects 0x0 outright)
20
+ * 2. Subrole penalty: AXFloatingWindow / AXDialog / AXSystemDialog are
21
+ * almost always popups; AXStandardWindow is preferred.
22
+ * 3. Title penalty: titles like "Downloads" / "Notifications" / "Updates"
23
+ * are commonly system-tray-ish popovers in apps like Xcode, Safari.
24
+ * 4. Z-order tiebreak: lower index ~= newer / more recently activated,
25
+ * so older-but-larger windows still beat tiny new popups via (1)+(2)+(3),
26
+ * but among equally-scored windows the older one wins (it's the window
27
+ * the user was already working in before the popup appeared).
28
+ *
29
+ * Bugs this addresses (clawdcursor #101):
30
+ * #2 focus_window ignored title= on multi-window apps and grabbed windows[0]
31
+ * #4 getActiveWindow grabbed windows[0] of the frontmost process, which on
32
+ * Xcode would surface the "Downloads" popup instead of the project window
33
+ */
34
+
35
+ function safeGet(obj, property, defaultValue) {
36
+ try {
37
+ var val = obj[property]();
38
+ return (val !== undefined && val !== null) ? val : defaultValue;
39
+ } catch (e) {
40
+ return defaultValue;
41
+ }
42
+ }
43
+
44
+ // Reads {x, y, width, height} for a window; returns zeros if AXPosition/AXSize
45
+ // are not accessible (some background processes return EACCESS here).
46
+ function getWinBounds(win) {
47
+ var bounds = { x: 0, y: 0, width: 0, height: 0 };
48
+ try {
49
+ var p = win.position();
50
+ var s = win.size();
51
+ if (p && s) {
52
+ bounds.x = p[0] || 0;
53
+ bounds.y = p[1] || 0;
54
+ bounds.width = s[0] || 0;
55
+ bounds.height = s[1] || 0;
56
+ }
57
+ } catch (e) {}
58
+ return bounds;
59
+ }
60
+
61
+ // Pulls the AXSubrole accessibility attribute, e.g. "AXStandardWindow",
62
+ // "AXFloatingWindow", "AXDialog", "AXSystemDialog". Returns '' if unknown.
63
+ function getWinSubrole(win) {
64
+ try {
65
+ return win.subrole() || '';
66
+ } catch (e) {}
67
+ try {
68
+ return win.attributes.byName('AXSubrole').value() || '';
69
+ } catch (e) {}
70
+ return '';
71
+ }
72
+
73
+ function isLikelyTrayTitle(title) {
74
+ if (!title) return false;
75
+ var t = String(title).trim().toLowerCase();
76
+ return t === 'downloads' || t === 'notifications' || t === 'updates' ||
77
+ t === 'inspector' || t === 'activity';
78
+ }
79
+
80
+ // Scores a window for "is this the user's real interaction window?".
81
+ // Higher score wins. Returns -Infinity for 0x0 / invisible windows.
82
+ function scoreWindow(win, index) {
83
+ var bounds = getWinBounds(win);
84
+ var area = bounds.width * bounds.height;
85
+ if (area <= 0) return -Infinity;
86
+
87
+ var score = area;
88
+
89
+ var subrole = getWinSubrole(win);
90
+ if (subrole === 'AXStandardWindow') {
91
+ score += 2000000; // strongly prefer standard windows
92
+ } else if (subrole === 'AXFloatingWindow') {
93
+ score -= 5000000; // popups, palettes
94
+ } else if (subrole === 'AXDialog' || subrole === 'AXSystemDialog') {
95
+ score -= 3000000; // modal dialogs
96
+ }
97
+
98
+ var title = '';
99
+ try { title = win.name() || ''; } catch (e) {}
100
+ if (isLikelyTrayTitle(title)) score -= 1500000;
101
+
102
+ // Tiebreak: older windows (higher index) get a tiny nudge so popups
103
+ // (which arrive at index 0) lose ties to the underlying work window.
104
+ score += index * 10;
105
+
106
+ return score;
107
+ }
108
+
109
+ // Picks the most likely user-interaction window from a window list.
110
+ // Returns { window, index } or null if no window has visible area.
111
+ function pickInteractionWindow(windows) {
112
+ var best = null;
113
+ var bestScore = -Infinity;
114
+ var bestIndex = -1;
115
+ for (var i = 0; i < windows.length; i++) {
116
+ try {
117
+ var w = windows[i];
118
+ var s = scoreWindow(w, i);
119
+ if (s > bestScore) {
120
+ bestScore = s;
121
+ best = w;
122
+ bestIndex = i;
123
+ }
124
+ } catch (e) {}
125
+ }
126
+ if (!best) return null;
127
+ return { window: best, index: bestIndex };
128
+ }
129
+
130
+ // Finds first window whose name contains `needle` (case-insensitive)
131
+ // AND has non-zero visible area. Used by focus-window.jxa.
132
+ function findTitleMatchWindow(windows, needle) {
133
+ if (!needle) return null;
134
+ var n = String(needle).toLowerCase();
135
+ for (var i = 0; i < windows.length; i++) {
136
+ try {
137
+ var w = windows[i];
138
+ var title = '';
139
+ try { title = w.name() || ''; } catch (e) {}
140
+ if (!title) continue;
141
+ if (title.toLowerCase().indexOf(n) === -1) continue;
142
+ var b = getWinBounds(w);
143
+ if (b.width * b.height <= 0) continue;
144
+ return { window: w, index: i, title: title };
145
+ } catch (e) {}
146
+ }
147
+ return null;
148
+ }
149
+
150
+ // This file is reference-only when invoked directly — print the helpers
151
+ // list so a curious developer running `osascript _window-picker.jxa` gets
152
+ // something useful back instead of nothing.
153
+ function run(argv) {
154
+ return JSON.stringify({
155
+ kind: 'reference',
156
+ helpers: [
157
+ 'safeGet', 'getWinBounds', 'getWinSubrole',
158
+ 'isLikelyTrayTitle', 'scoreWindow',
159
+ 'pickInteractionWindow', 'findTitleMatchWindow'
160
+ ],
161
+ note: 'osascript cannot require() — these helpers are inlined in focus-window.jxa and get-foreground-window.jxa. Keep all three in sync.'
162
+ });
163
+ }
Binary file
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env bash
2
+ # find-element.sh — Find UI elements on macOS using JXA + System Events
3
+ #
4
+ # Wrapper around find-element.jxa for UIDriver cross-platform support.
5
+ # Outputs JSON matching the Windows find-element.ps1 format:
6
+ # [{ name, automationId, controlType, className, processId, isEnabled, bounds }]
7
+ #
8
+ # Parameters:
9
+ # -Name <string> Match elements by name (contains, case-insensitive)
10
+ # -AutomationId <string> Match by description/identifier (mapped to name search)
11
+ # -ControlType <string> Match by role (e.g. Button, Edit, MenuItem)
12
+ # -ProcessId <number> Limit to a specific process
13
+ # -MaxResults <number> Max results (default 20)
14
+ #
15
+ # Usage:
16
+ # chmod +x scripts/mac/find-element.sh
17
+ # ./scripts/mac/find-element.sh -Name "OK" -ControlType "Button" -ProcessId 1234
18
+
19
+ set -euo pipefail
20
+
21
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
22
+
23
+ # Parse arguments
24
+ NAME=""
25
+ AUTOMATION_ID=""
26
+ CONTROL_TYPE=""
27
+ PROCESS_ID=""
28
+ MAX_RESULTS="20"
29
+
30
+ while [[ $# -gt 0 ]]; do
31
+ case "$1" in
32
+ -Name) NAME="$2"; shift 2 ;;
33
+ -AutomationId) AUTOMATION_ID="$2"; shift 2 ;;
34
+ -ControlType) CONTROL_TYPE="$2"; shift 2 ;;
35
+ -ProcessId) PROCESS_ID="$2"; shift 2 ;;
36
+ -MaxResults) MAX_RESULTS="$2"; shift 2 ;;
37
+ *) shift ;;
38
+ esac
39
+ done
40
+
41
+ # Map Windows ControlType names to macOS accessibility roles
42
+ map_control_type() {
43
+ local ct="$1"
44
+ case "$ct" in
45
+ Button) echo "button" ;;
46
+ Edit) echo "text field" ;;
47
+ Text) echo "static text" ;;
48
+ CheckBox) echo "checkbox" ;;
49
+ RadioButton) echo "radio button" ;;
50
+ ComboBox) echo "pop up button" ;;
51
+ MenuItem) echo "menu item" ;;
52
+ Menu) echo "menu" ;;
53
+ MenuBar) echo "menu bar" ;;
54
+ List) echo "list" ;;
55
+ ListItem) echo "row" ;;
56
+ Tree) echo "outline" ;;
57
+ TreeItem) echo "row" ;;
58
+ Tab) echo "tab group" ;;
59
+ TabItem) echo "radio button" ;;
60
+ Slider) echo "slider" ;;
61
+ ProgressBar) echo "progress indicator" ;;
62
+ ToolBar) echo "toolbar" ;;
63
+ Window) echo "window" ;;
64
+ Group) echo "group" ;;
65
+ Image) echo "image" ;;
66
+ Hyperlink) echo "link" ;;
67
+ ScrollBar) echo "scroll bar" ;;
68
+ Table) echo "table" ;;
69
+ Document) echo "text area" ;;
70
+ *) echo "$ct" ;;
71
+ esac
72
+ }
73
+
74
+ # Build JXA arguments
75
+ JXA_ARGS=()
76
+
77
+ # Use Name or AutomationId as the search name
78
+ SEARCH_NAME="${NAME:-$AUTOMATION_ID}"
79
+ if [[ -n "$SEARCH_NAME" ]]; then
80
+ JXA_ARGS+=("-name" "$SEARCH_NAME")
81
+ fi
82
+
83
+ # Map control type to macOS role
84
+ if [[ -n "$CONTROL_TYPE" ]]; then
85
+ MAPPED_ROLE=$(map_control_type "$CONTROL_TYPE")
86
+ JXA_ARGS+=("-role" "$MAPPED_ROLE")
87
+ fi
88
+
89
+ if [[ -n "$PROCESS_ID" && "$PROCESS_ID" != "0" ]]; then
90
+ JXA_ARGS+=("-processId" "$PROCESS_ID")
91
+ fi
92
+
93
+ # Run the JXA script
94
+ RAW_OUTPUT=$(osascript -l JavaScript "$SCRIPT_DIR/find-element.jxa" "${JXA_ARGS[@]}" 2>/dev/null || echo "[]")
95
+
96
+ # Transform the JXA output to match Windows format:
97
+ # JXA returns: { name, role, value, bounds, processId }
98
+ # Windows expects: { name, automationId, controlType, className, processId, isEnabled, bounds }
99
+ echo "$RAW_OUTPUT" | /usr/bin/python3 -c "
100
+ import sys, json
101
+
102
+ try:
103
+ data = json.load(sys.stdin)
104
+ except:
105
+ print('[]')
106
+ sys.exit(0)
107
+
108
+ if isinstance(data, dict) and 'error' in data:
109
+ print(json.dumps(data))
110
+ sys.exit(0)
111
+
112
+ if not isinstance(data, list):
113
+ data = [data]
114
+
115
+ # Map macOS roles back to Windows ControlType names
116
+ ROLE_MAP = {
117
+ 'AXButton': 'Button', 'button': 'Button',
118
+ 'AXTextField': 'Edit', 'text field': 'Edit',
119
+ 'AXStaticText': 'Text', 'static text': 'Text',
120
+ 'AXCheckBox': 'CheckBox', 'checkbox': 'CheckBox',
121
+ 'AXRadioButton': 'RadioButton', 'radio button': 'RadioButton',
122
+ 'AXPopUpButton': 'ComboBox', 'pop up button': 'ComboBox',
123
+ 'AXMenuItem': 'MenuItem', 'menu item': 'MenuItem',
124
+ 'AXMenu': 'Menu', 'menu': 'Menu',
125
+ 'AXMenuBar': 'MenuBar', 'menu bar': 'MenuBar',
126
+ 'AXList': 'List', 'list': 'List',
127
+ 'AXRow': 'ListItem', 'row': 'ListItem',
128
+ 'AXOutline': 'Tree', 'outline': 'Tree',
129
+ 'AXTabGroup': 'Tab', 'tab group': 'Tab',
130
+ 'AXSlider': 'Slider', 'slider': 'Slider',
131
+ 'AXProgressIndicator': 'ProgressBar', 'progress indicator': 'ProgressBar',
132
+ 'AXToolbar': 'ToolBar', 'toolbar': 'ToolBar',
133
+ 'AXWindow': 'Window', 'window': 'Window',
134
+ 'AXGroup': 'Group', 'group': 'Group',
135
+ 'AXImage': 'Image', 'image': 'Image',
136
+ 'AXLink': 'Hyperlink', 'link': 'Hyperlink',
137
+ 'AXScrollBar': 'ScrollBar', 'scroll bar': 'ScrollBar',
138
+ 'AXTable': 'Table', 'table': 'Table',
139
+ 'AXTextArea': 'Document', 'text area': 'Document',
140
+ 'AXScrollArea': 'Pane', 'scroll area': 'Pane',
141
+ }
142
+
143
+ max_results = int('${MAX_RESULTS}')
144
+ results = []
145
+
146
+ for item in data[:max_results]:
147
+ role = item.get('role', '')
148
+ control_type = ROLE_MAP.get(role, role)
149
+
150
+ results.append({
151
+ 'name': item.get('name', ''),
152
+ 'automationId': '', # macOS doesn't have AutomationId; use identifier if available
153
+ 'controlType': control_type,
154
+ 'className': role, # Store original macOS role as className
155
+ 'processId': item.get('processId', 0),
156
+ 'isEnabled': True, # macOS doesn't easily expose enabled state in bulk
157
+ 'bounds': item.get('bounds', {'x': 0, 'y': 0, 'width': 0, 'height': 0})
158
+ })
159
+
160
+ print(json.dumps(results, separators=(',', ':')))
161
+ "