@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,284 @@
1
+ #!/usr/bin/env osascript -l JavaScript
2
+
3
+ /**
4
+ * focus-window.jxa
5
+ *
6
+ * Brings a window to the front by title substring or process ID.
7
+ *
8
+ * Parameters:
9
+ * -title <string> - Substring match against window titles (case-insensitive)
10
+ * -processId <number> - Process ID to focus
11
+ *
12
+ * Returns JSON result with success status and window info.
13
+ *
14
+ * Note: At least one of -title or -processId must be specified.
15
+ *
16
+ * When BOTH are supplied, -title is used to disambiguate among that
17
+ * process's windows (multi-window apps like Xcode, VS Code, Chrome).
18
+ * The previous implementation always picked process.windows[0], which
19
+ * surfaced whichever popup was most recent (e.g. Xcode's "Downloads"
20
+ * popover at 1760,80 instead of the user's project window). See bug
21
+ * #2 of clawdcursor issue #101.
22
+ *
23
+ * Usage: osascript -l JavaScript focus-window.jxa -title "Safari"
24
+ * osascript -l JavaScript focus-window.jxa -processId 1234
25
+ * osascript -l JavaScript focus-window.jxa -processId 1234 -title "Amr Dabbas"
26
+ */
27
+
28
+ // ── Inlined from scripts/mac/_window-picker.jxa ───────────────────
29
+ // Keep these helpers in sync with _window-picker.jxa; osascript can't require().
30
+
31
+ function safeGet(obj, property, defaultValue) {
32
+ try {
33
+ var val = obj[property]();
34
+ return (val !== undefined && val !== null) ? val : defaultValue;
35
+ } catch (e) {
36
+ return defaultValue;
37
+ }
38
+ }
39
+
40
+ function getWinBounds(win) {
41
+ var bounds = { x: 0, y: 0, width: 0, height: 0 };
42
+ try {
43
+ var p = win.position();
44
+ var s = win.size();
45
+ if (p && s) {
46
+ bounds.x = p[0] || 0;
47
+ bounds.y = p[1] || 0;
48
+ bounds.width = s[0] || 0;
49
+ bounds.height = s[1] || 0;
50
+ }
51
+ } catch (e) {}
52
+ return bounds;
53
+ }
54
+
55
+ function getWinSubrole(win) {
56
+ try { return win.subrole() || ''; } catch (e) {}
57
+ try { return win.attributes.byName('AXSubrole').value() || ''; } catch (e) {}
58
+ return '';
59
+ }
60
+
61
+ function isLikelyTrayTitle(title) {
62
+ if (!title) return false;
63
+ var t = String(title).trim().toLowerCase();
64
+ return t === 'downloads' || t === 'notifications' || t === 'updates' ||
65
+ t === 'inspector' || t === 'activity';
66
+ }
67
+
68
+ function scoreWindow(win, index) {
69
+ var bounds = getWinBounds(win);
70
+ var area = bounds.width * bounds.height;
71
+ if (area <= 0) return -Infinity;
72
+ var score = area;
73
+ var subrole = getWinSubrole(win);
74
+ if (subrole === 'AXStandardWindow') score += 2000000;
75
+ else if (subrole === 'AXFloatingWindow') score -= 5000000;
76
+ else if (subrole === 'AXDialog' || subrole === 'AXSystemDialog') score -= 3000000;
77
+ var title = '';
78
+ try { title = win.name() || ''; } catch (e) {}
79
+ if (isLikelyTrayTitle(title)) score -= 1500000;
80
+ score += index * 10;
81
+ return score;
82
+ }
83
+
84
+ function pickInteractionWindow(windows) {
85
+ var best = null, bestScore = -Infinity, bestIndex = -1;
86
+ for (var i = 0; i < windows.length; i++) {
87
+ try {
88
+ var s = scoreWindow(windows[i], i);
89
+ if (s > bestScore) { bestScore = s; best = windows[i]; bestIndex = i; }
90
+ } catch (e) {}
91
+ }
92
+ if (!best) return null;
93
+ return { window: best, index: bestIndex };
94
+ }
95
+
96
+ function findTitleMatchWindow(windows, needle) {
97
+ if (!needle) return null;
98
+ var n = String(needle).toLowerCase();
99
+ for (var i = 0; i < windows.length; i++) {
100
+ try {
101
+ var w = windows[i];
102
+ var title = '';
103
+ try { title = w.name() || ''; } catch (e) {}
104
+ if (!title) continue;
105
+ if (title.toLowerCase().indexOf(n) === -1) continue;
106
+ var b = getWinBounds(w);
107
+ if (b.width * b.height <= 0) continue;
108
+ return { window: w, index: i, title: title };
109
+ } catch (e) {}
110
+ }
111
+ return null;
112
+ }
113
+
114
+ // ── End inlined helpers ───────────────────────────────────────────
115
+
116
+ // run(argv) is the JXA entry point — osascript calls it and its return value becomes stdout.
117
+ // argv receives command-line arguments passed after the script path.
118
+ function run(argv) {
119
+ try {
120
+ // Parse arguments from argv array
121
+ var params = { title: null, processId: null };
122
+ for (var i = 0; i < argv.length; i++) {
123
+ var a = argv[i];
124
+ var hasNext = i + 1 < argv.length;
125
+ if ((a === '-title' || a === '-Title') && hasNext) {
126
+ params.title = argv[++i];
127
+ } else if ((a === '-processId' || a === '-ProcessId') && hasNext) {
128
+ params.processId = parseInt(argv[++i], 10);
129
+ }
130
+ // -Restore has no direct macOS equivalent; silently ignore
131
+ }
132
+
133
+ // Validate parameters
134
+ if (!params.title && !params.processId) {
135
+ return JSON.stringify({
136
+ success: false,
137
+ error: 'Must specify -title or -processId'
138
+ });
139
+ }
140
+
141
+ var SystemEvents = Application('System Events');
142
+ SystemEvents.includeStandardAdditions = true;
143
+
144
+ // Get all processes with UI
145
+ var processes = SystemEvents.processes.where({ backgroundOnly: false });
146
+ var targetWindow = null;
147
+ var targetProcess = null;
148
+
149
+ // Phase 1: if processId is supplied, locate that process and pick
150
+ // the right window inside it. This is the bug #2 fix — when title
151
+ // is also supplied, the title MUST be consulted before defaulting
152
+ // to windows[0] (which on multi-window apps is often a popup).
153
+ if (params.processId) {
154
+ for (var pi = 0; pi < processes.length; pi++) {
155
+ try {
156
+ var process = processes[pi];
157
+ var pidHere = safeGet(process, 'unixId', 0);
158
+ if (pidHere !== params.processId) continue;
159
+
160
+ targetProcess = process;
161
+ var windows = process.windows;
162
+
163
+ if (windows.length === 0) break;
164
+
165
+ // (a) Title-disambiguation inside the matched process
166
+ if (params.title) {
167
+ var hit = findTitleMatchWindow(windows, params.title);
168
+ if (hit) { targetWindow = hit.window; break; }
169
+ }
170
+
171
+ // (b) No title or no title match — fall back to the
172
+ // scored interaction window rather than blindly windows[0],
173
+ // which would re-introduce the popup-grab regression.
174
+ var picked = pickInteractionWindow(windows);
175
+ if (picked) { targetWindow = picked.window; break; }
176
+
177
+ // (c) Last resort: keep legacy behaviour so we never
178
+ // regress to "matched process but no window".
179
+ targetWindow = windows[0];
180
+ break;
181
+ } catch (e) {
182
+ // Process not accessible
183
+ }
184
+ }
185
+ }
186
+
187
+ // Phase 2: cross-process title scan, used when processId wasn't
188
+ // supplied or didn't match. Bug #2's second clause: this scan must
189
+ // skip 0x0 (invisible) windows that happen to share the title but
190
+ // can't actually be raised — those return success but do nothing.
191
+ if (!targetWindow && params.title) {
192
+ for (var pi2 = 0; pi2 < processes.length; pi2++) {
193
+ try {
194
+ var p2 = processes[pi2];
195
+ var ws = p2.windows;
196
+ var hit2 = findTitleMatchWindow(ws, params.title);
197
+ if (hit2) {
198
+ targetWindow = hit2.window;
199
+ targetProcess = p2;
200
+ break;
201
+ }
202
+ } catch (e) {
203
+ // Process not accessible
204
+ }
205
+ }
206
+ }
207
+
208
+ if (!targetWindow) {
209
+ return JSON.stringify({
210
+ success: false,
211
+ error: 'Window not found matching title="' + (params.title || '') + '" processId=' + (params.processId || 0)
212
+ });
213
+ }
214
+
215
+ // Bring window to front
216
+ // Method 1: Use System Events to set frontmost
217
+ try {
218
+ if (targetProcess) {
219
+ targetProcess.frontmost = true;
220
+ }
221
+ } catch (e) {
222
+ // Continue with other methods
223
+ }
224
+
225
+ // Method 2: Focus the window
226
+ try {
227
+ targetWindow.focused = true;
228
+ } catch (e) {
229
+ // Continue
230
+ }
231
+
232
+ // Method 3: Use application level activation (if we can get the app name)
233
+ try {
234
+ if (targetProcess) {
235
+ var appName = safeGet(targetProcess, 'name', '');
236
+ if (appName) {
237
+ var app = Application(appName);
238
+ app.activate();
239
+ }
240
+ }
241
+ } catch (e) {
242
+ // Continue
243
+ }
244
+
245
+ // Method 4: AXRaise — the only call that, on multi-window apps, brings
246
+ // the SPECIFIC window forward instead of just activating the app and
247
+ // letting macOS surface whichever window was frontmost last. Without
248
+ // this, bug #2 would only be half-fixed: we'd pick the right window
249
+ // but app.activate() would still raise the app's most-recent window.
250
+ try {
251
+ targetWindow.actions.byName('AXRaise').perform();
252
+ } catch (e) {
253
+ // Not all windows expose AXRaise; ignore if unavailable.
254
+ }
255
+
256
+ // Get final window info
257
+ var winTitle = '';
258
+ var winProcessId = 0;
259
+ var winProcessName = 'unknown';
260
+ var winBounds = getWinBounds(targetWindow);
261
+ var winSubrole = getWinSubrole(targetWindow);
262
+
263
+ try {
264
+ winTitle = targetWindow.name() || '';
265
+ } catch (e) {}
266
+
267
+ if (targetProcess) {
268
+ winProcessId = safeGet(targetProcess, 'unixId', 0);
269
+ winProcessName = safeGet(targetProcess, 'name', 'unknown');
270
+ }
271
+
272
+ return JSON.stringify({
273
+ success: true,
274
+ title: winTitle,
275
+ processId: winProcessId,
276
+ processName: winProcessName,
277
+ bounds: winBounds,
278
+ subrole: winSubrole
279
+ });
280
+
281
+ } catch (error) {
282
+ return JSON.stringify({ success: false, error: error.toString() });
283
+ }
284
+ }
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env osascript -l JavaScript
2
+ /**
3
+ * get-focused-element.jxa — returns the currently focused UI element on macOS.
4
+ *
5
+ * Output: JSON with name, role, value, bounds, processId.
6
+ * Matches the FocusedElementInfo shape from accessibility.ts.
7
+ */
8
+
9
+ function run() {
10
+ const se = Application('System Events');
11
+ se.includeStandardAdditions = true;
12
+
13
+ try {
14
+ const procs = se.applicationProcesses.whose({ frontmost: true });
15
+ if (procs.length === 0) {
16
+ return JSON.stringify(null);
17
+ }
18
+
19
+ const proc = procs[0];
20
+ const pid = proc.unixId();
21
+ const procName = proc.name();
22
+
23
+ // Get the focused UI element
24
+ let focused;
25
+ try {
26
+ focused = proc.focusedUIElement();
27
+ } catch (e) {
28
+ // Some apps don't expose focused element — return window info instead
29
+ try {
30
+ const wins = proc.windows();
31
+ if (wins.length > 0) {
32
+ const win = wins[0];
33
+ const pos = win.position();
34
+ const sz = win.size();
35
+ return JSON.stringify({
36
+ name: win.name() || procName,
37
+ automationId: '',
38
+ controlType: 'Window',
39
+ className: procName,
40
+ processId: pid,
41
+ isEnabled: true,
42
+ bounds: { x: pos[0], y: pos[1], width: sz[0], height: sz[1] },
43
+ value: ''
44
+ });
45
+ }
46
+ } catch (_) {}
47
+ return JSON.stringify(null);
48
+ }
49
+
50
+ // Extract properties safely
51
+ let name = '', role = '', value = '';
52
+ let bounds = { x: 0, y: 0, width: 0, height: 0 };
53
+
54
+ try { name = focused.name() || ''; } catch (_) {}
55
+ try { role = focused.role() || ''; } catch (_) {}
56
+ try { value = String(focused.value() || ''); } catch (_) {}
57
+
58
+ try {
59
+ const pos = focused.position();
60
+ const sz = focused.size();
61
+ bounds = { x: pos[0], y: pos[1], width: sz[0], height: sz[1] };
62
+ } catch (_) {}
63
+
64
+ // Map AX roles to Windows ControlType names for compatibility
65
+ const roleMap = {
66
+ 'AXTextField': 'Edit',
67
+ 'AXTextArea': 'Edit',
68
+ 'AXButton': 'Button',
69
+ 'AXCheckBox': 'CheckBox',
70
+ 'AXRadioButton': 'RadioButton',
71
+ 'AXComboBox': 'ComboBox',
72
+ 'AXList': 'List',
73
+ 'AXTable': 'Table',
74
+ 'AXMenu': 'Menu',
75
+ 'AXMenuItem': 'MenuItem',
76
+ 'AXStaticText': 'Text',
77
+ 'AXImage': 'Image',
78
+ 'AXLink': 'Hyperlink',
79
+ 'AXGroup': 'Group',
80
+ 'AXWindow': 'Window',
81
+ 'AXWebArea': 'Document',
82
+ 'AXScrollArea': 'ScrollBar',
83
+ 'AXTab': 'Tab',
84
+ 'AXSlider': 'Slider',
85
+ };
86
+
87
+ const controlType = roleMap[role] || role.replace('AX', '') || 'Unknown';
88
+
89
+ return JSON.stringify({
90
+ name: name,
91
+ automationId: '',
92
+ controlType: controlType,
93
+ className: procName,
94
+ processId: pid,
95
+ isEnabled: true,
96
+ bounds: bounds,
97
+ value: value
98
+ });
99
+ } catch (e) {
100
+ return JSON.stringify(null);
101
+ }
102
+ }
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env osascript -l JavaScript
2
+
3
+ /**
4
+ * get-foreground-window.jxa
5
+ *
6
+ * Gets the currently focused/foreground window.
7
+ *
8
+ * Returns JSON with:
9
+ * - handle: Window identifier (on macOS this is the process ID as a proxy)
10
+ * - processId: Unix process ID of the foreground application
11
+ * - processName: Name of the foreground application
12
+ * - title: Title of the chosen window
13
+ * - bounds: {x, y, width, height} of the chosen window
14
+ * - subrole: AXSubrole of the chosen window ("AXStandardWindow", etc.)
15
+ * - success: Boolean indicating success
16
+ *
17
+ * Bug #4 of clawdcursor issue #101: the previous implementation took
18
+ * processes.where({frontmost: true})[0].windows[0], which on apps that
19
+ * spawn a popup (Xcode "Downloads", Safari "Notifications") returned the
20
+ * popup instead of the user's actual interaction window. We now score
21
+ * each window of the frontmost process and pick the best one.
22
+ *
23
+ * Usage: osascript -l JavaScript get-foreground-window.jxa
24
+ */
25
+
26
+ // ── Inlined from scripts/mac/_window-picker.jxa ───────────────────
27
+ // Keep these helpers in sync with _window-picker.jxa; osascript can't require().
28
+
29
+ function safeGet(obj, property, defaultValue) {
30
+ try {
31
+ var val = obj[property]();
32
+ return (val !== undefined && val !== null) ? val : defaultValue;
33
+ } catch (e) {
34
+ return defaultValue;
35
+ }
36
+ }
37
+
38
+ function getWinBounds(win) {
39
+ var bounds = { x: 0, y: 0, width: 0, height: 0 };
40
+ try {
41
+ var p = win.position();
42
+ var s = win.size();
43
+ if (p && s) {
44
+ bounds.x = p[0] || 0;
45
+ bounds.y = p[1] || 0;
46
+ bounds.width = s[0] || 0;
47
+ bounds.height = s[1] || 0;
48
+ }
49
+ } catch (e) {}
50
+ return bounds;
51
+ }
52
+
53
+ function getWinSubrole(win) {
54
+ try { return win.subrole() || ''; } catch (e) {}
55
+ try { return win.attributes.byName('AXSubrole').value() || ''; } catch (e) {}
56
+ return '';
57
+ }
58
+
59
+ function isLikelyTrayTitle(title) {
60
+ if (!title) return false;
61
+ var t = String(title).trim().toLowerCase();
62
+ return t === 'downloads' || t === 'notifications' || t === 'updates' ||
63
+ t === 'inspector' || t === 'activity';
64
+ }
65
+
66
+ function scoreWindow(win, index) {
67
+ var bounds = getWinBounds(win);
68
+ var area = bounds.width * bounds.height;
69
+ if (area <= 0) return -Infinity;
70
+ var score = area;
71
+ var subrole = getWinSubrole(win);
72
+ if (subrole === 'AXStandardWindow') score += 2000000;
73
+ else if (subrole === 'AXFloatingWindow') score -= 5000000;
74
+ else if (subrole === 'AXDialog' || subrole === 'AXSystemDialog') score -= 3000000;
75
+ var title = '';
76
+ try { title = win.name() || ''; } catch (e) {}
77
+ if (isLikelyTrayTitle(title)) score -= 1500000;
78
+ score += index * 10;
79
+ return score;
80
+ }
81
+
82
+ function pickInteractionWindow(windows) {
83
+ var best = null, bestScore = -Infinity, bestIndex = -1;
84
+ for (var i = 0; i < windows.length; i++) {
85
+ try {
86
+ var s = scoreWindow(windows[i], i);
87
+ if (s > bestScore) { bestScore = s; best = windows[i]; bestIndex = i; }
88
+ } catch (e) {}
89
+ }
90
+ if (!best) return null;
91
+ return { window: best, index: bestIndex };
92
+ }
93
+
94
+ // ── End inlined helpers ───────────────────────────────────────────
95
+
96
+ // run(argv) is the JXA entry point — osascript calls it and its return value becomes stdout.
97
+ function run(argv) {
98
+ try {
99
+ var SystemEvents = Application('System Events');
100
+ SystemEvents.includeStandardAdditions = true;
101
+
102
+ // Get the frontmost process
103
+ var frontProcesses = SystemEvents.processes.where({ frontmost: true });
104
+
105
+ if (frontProcesses.length === 0) {
106
+ return JSON.stringify({
107
+ success: false,
108
+ error: 'No foreground window found'
109
+ });
110
+ }
111
+
112
+ var frontProcess = frontProcesses[0];
113
+ var processId = safeGet(frontProcess, 'unixId', 0);
114
+ var processName = safeGet(frontProcess, 'name', 'unknown');
115
+
116
+ // Pick the user's real interaction window instead of windows[0].
117
+ // See header comment for bug #4 rationale.
118
+ var title = '';
119
+ var bounds = { x: 0, y: 0, width: 0, height: 0 };
120
+ var subrole = '';
121
+
122
+ try {
123
+ var windows = frontProcess.windows;
124
+ if (windows.length > 0) {
125
+ var picked = pickInteractionWindow(windows);
126
+ var chosen = null;
127
+
128
+ if (picked) {
129
+ chosen = picked.window;
130
+ } else {
131
+ // No window had visible area — try a focused window as a
132
+ // last resort before giving up on title/bounds entirely.
133
+ for (var i = 0; i < windows.length; i++) {
134
+ try {
135
+ if (windows[i].focused && windows[i].focused()) {
136
+ chosen = windows[i];
137
+ break;
138
+ }
139
+ } catch (e) {}
140
+ }
141
+ // If still nothing, fall back to windows[0] purely for
142
+ // title extraction — preserves prior behaviour when the
143
+ // only available window is a popup.
144
+ if (!chosen) chosen = windows[0];
145
+ }
146
+
147
+ try { title = chosen.name() || ''; } catch (e) {}
148
+ bounds = getWinBounds(chosen);
149
+ subrole = getWinSubrole(chosen);
150
+ }
151
+ } catch (e) {
152
+ // No windows accessible
153
+ }
154
+
155
+ // On macOS, we don't have window handles like Win32 HWND
156
+ // We use processId as a proxy identifier
157
+ return JSON.stringify({
158
+ success: true,
159
+ handle: processId, // Using processId as handle equivalent
160
+ processId: processId,
161
+ processName: processName,
162
+ title: title,
163
+ bounds: bounds,
164
+ subrole: subrole
165
+ });
166
+
167
+ } catch (error) {
168
+ return JSON.stringify({
169
+ success: false,
170
+ error: error.toString()
171
+ });
172
+ }
173
+ }