skydeckai-code 0.1.23__py3-none-any.whl

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.
@@ -0,0 +1,395 @@
1
+ import json
2
+ import platform
3
+ import subprocess
4
+ from typing import Any, Dict, List
5
+ import importlib.util
6
+
7
+ from mcp import types
8
+
9
+ # Use importlib.util.find_spec to check for availability of optional packages
10
+ def is_package_available(package_name):
11
+ """Check if a package is available using importlib.util.find_spec."""
12
+ return importlib.util.find_spec(package_name) is not None
13
+
14
+ # Check for PyGetWindow
15
+ PYGETWINDOW_AVAILABLE = is_package_available("pygetwindow")
16
+
17
+ # Check for macOS-specific Quartz framework
18
+ QUARTZ_AVAILABLE = False
19
+ if platform.system().lower() == "darwin":
20
+ QUARTZ_AVAILABLE = is_package_available("Quartz")
21
+ if QUARTZ_AVAILABLE:
22
+ from Quartz import (
23
+ CGWindowListCopyWindowInfo,
24
+ kCGNullWindowID,
25
+ kCGWindowListOptionOnScreenOnly,
26
+ )
27
+
28
+
29
+ def get_available_windows_tool():
30
+ """Define the get_available_windows tool."""
31
+ return {
32
+ "name": "get_available_windows",
33
+ "description": "Get detailed information about all available windows currently displayed on the user's screen. "
34
+ "WHEN TO USE: When you need to know exactly what windows are visible to the user, find a specific "
35
+ "window by title, provide guidance related to something the user is viewing, or need window-level "
36
+ "context that's more detailed than application-level information. Useful for referencing specific "
37
+ "content the user can see on their screen. "
38
+ "WHEN NOT TO USE: When application-level information is sufficient (use get_active_apps instead), "
39
+ "when you need to capture what's on screen (use capture_screenshot instead), or when window "
40
+ "context isn't relevant to the task at hand. "
41
+ "RETURNS: JSON object containing platform information, success status, count of windows, and an "
42
+ "array of window objects. Each window object includes title, application owner, visibility status, "
43
+ "and platform-specific details like window IDs. Works on macOS, Windows, and Linux, with "
44
+ "platform-specific implementation details.",
45
+ "inputSchema": {
46
+ "type": "object",
47
+ "properties": {},
48
+ "required": []
49
+ },
50
+ }
51
+
52
+
53
+ def _get_windows_macos() -> List[Dict[str, Any]]:
54
+ """
55
+ Get information about all available windows on macOS.
56
+
57
+ Returns:
58
+ List of dictionaries containing window information
59
+ """
60
+ windows = []
61
+
62
+ if not QUARTZ_AVAILABLE:
63
+ print("Quartz framework not available. Unable to list windows on macOS.")
64
+ return windows
65
+
66
+ try:
67
+ # Get the list of windows from Quartz
68
+ window_list = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID)
69
+
70
+ for window in window_list:
71
+ window_id = window.get('kCGWindowNumber', 0)
72
+ owner = window.get('kCGWindowOwnerName', '')
73
+ name = window.get('kCGWindowName', '')
74
+ layer = window.get('kCGWindowLayer', 0)
75
+ alpha = window.get('kCGWindowAlpha', 1.0)
76
+
77
+ # Create the window info dictionary
78
+ window_info = {
79
+ "id": window_id,
80
+ "title": name,
81
+ "app": owner,
82
+ "visible": layer <= 0 and alpha > 0.1,
83
+ }
84
+
85
+ windows.append(window_info)
86
+
87
+ # Sort windows by application name and then by window title
88
+ windows.sort(key=lambda w: (w.get("app", "").lower(), w.get("title", "").lower()))
89
+
90
+ except Exception as e:
91
+ print(f"Error getting windows on macOS: {str(e)}")
92
+
93
+ return windows
94
+
95
+
96
+ def _get_windows_windows() -> List[Dict[str, Any]]:
97
+ """
98
+ Get information about all available windows on Windows.
99
+
100
+ Returns:
101
+ List of dictionaries containing window information
102
+ """
103
+ windows = []
104
+
105
+ # Try using PyGetWindow if available
106
+ if PYGETWINDOW_AVAILABLE:
107
+ try:
108
+ import pygetwindow as gw
109
+ all_windows = gw.getAllWindows()
110
+
111
+ for window in all_windows:
112
+ # Skip windows with empty titles
113
+ if not window.title:
114
+ continue
115
+
116
+ # Try to determine the application name from the window title
117
+ # This is an approximation and may not be accurate for all applications
118
+ app_name = ""
119
+ title_parts = window.title.split(' - ')
120
+ if len(title_parts) > 1:
121
+ app_name = title_parts[-1]
122
+
123
+ # Create the window info dictionary
124
+ window_info = {
125
+ "title": window.title,
126
+ "visible": window.visible,
127
+ "active": window.isActive
128
+ }
129
+
130
+ # Add app name if we were able to determine it
131
+ if app_name:
132
+ window_info["app"] = app_name
133
+
134
+ windows.append(window_info)
135
+
136
+ # Sort windows by application name and then by window title
137
+ windows.sort(key=lambda w: (w.get("app", "").lower() if "app" in w else "", w.get("title", "").lower()))
138
+
139
+ except Exception as e:
140
+ print(f"Error getting windows with PyGetWindow: {str(e)}")
141
+
142
+ # If PyGetWindow failed or isn't available, try using PowerShell
143
+ if not windows:
144
+ try:
145
+ script = '''
146
+ Add-Type @"
147
+ using System;
148
+ using System.Runtime.InteropServices;
149
+ using System.Text;
150
+
151
+ public class Window {
152
+ [DllImport("user32.dll")]
153
+ [return: MarshalAs(UnmanagedType.Bool)]
154
+ public static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
155
+
156
+ [DllImport("user32.dll")]
157
+ public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
158
+
159
+ [DllImport("user32.dll")]
160
+ public static extern bool IsWindowVisible(IntPtr hWnd);
161
+
162
+ [DllImport("user32.dll", SetLastError=true)]
163
+ public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
164
+
165
+ public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
166
+ }
167
+ "@
168
+
169
+ $windows = @()
170
+
171
+ $enumWindowsCallback = {
172
+ param($hwnd, $lParam)
173
+
174
+ # Get the window title
175
+ $sb = New-Object System.Text.StringBuilder(256)
176
+ [void][Window]::GetWindowText($hwnd, $sb, $sb.Capacity)
177
+ $title = $sb.ToString()
178
+
179
+ # Only process windows with titles
180
+ if($title -and $title -ne "") {
181
+ # Check if the window is visible
182
+ $visible = [Window]::IsWindowVisible($hwnd)
183
+
184
+ # Get process ID and name
185
+ $processId = 0
186
+ [void][Window]::GetWindowThreadProcessId($hwnd, [ref]$processId)
187
+ $process = Get-Process -Id $processId -ErrorAction SilentlyContinue
188
+ $processName = if($process) { $process.ProcessName } else { "Unknown" }
189
+
190
+ # Create the window object
191
+ $window = @{
192
+ title = $title
193
+ app = $processName
194
+ visible = $visible
195
+ }
196
+
197
+ $windows += $window
198
+ }
199
+
200
+ # Continue enumeration
201
+ return $true
202
+ }
203
+
204
+ # Enumerate all windows
205
+ [void][Window]::EnumWindows($enumWindowsCallback, [IntPtr]::Zero)
206
+
207
+ # Sort the windows
208
+ $windows = $windows | Sort-Object -Property @{Expression="app"}, @{Expression="title"}
209
+
210
+ # Convert to JSON
211
+ $windows | ConvertTo-Json -Depth 3
212
+ '''
213
+
214
+ cmd = ["powershell", "-Command", script]
215
+ process = subprocess.run(cmd, capture_output=True, text=True)
216
+
217
+ if process.returncode == 0 and process.stdout.strip():
218
+ try:
219
+ windows_data = json.loads(process.stdout)
220
+ # Handle single item (not in a list)
221
+ if isinstance(windows_data, dict):
222
+ windows_data = [windows_data]
223
+
224
+ windows = windows_data
225
+ except json.JSONDecodeError:
226
+ print("Failed to parse JSON from PowerShell output")
227
+
228
+ except Exception as e:
229
+ print(f"Error getting windows with PowerShell: {str(e)}")
230
+
231
+ return windows
232
+
233
+
234
+ def _get_windows_linux() -> List[Dict[str, Any]]:
235
+ """
236
+ Get information about all available windows on Linux.
237
+
238
+ Returns:
239
+ List of dictionaries containing window information
240
+ """
241
+ windows = []
242
+
243
+ # Try using wmctrl if available
244
+ try:
245
+ # Check if wmctrl is installed
246
+ check_process = subprocess.run(["which", "wmctrl"], capture_output=True)
247
+ if check_process.returncode == 0:
248
+ # Get the list of windows
249
+ wmctrl_process = subprocess.run(["wmctrl", "-l"], capture_output=True, text=True)
250
+
251
+ if wmctrl_process.returncode == 0:
252
+ window_data = wmctrl_process.stdout.strip().split('\n')
253
+
254
+ for line in window_data:
255
+ if not line:
256
+ continue
257
+
258
+ parts = line.split(None, 3)
259
+ if len(parts) < 4:
260
+ continue
261
+
262
+ window_id, desktop, owner, *title_parts = parts
263
+ title = title_parts[0] if title_parts else ""
264
+
265
+ # Create the window info dictionary
266
+ window_info = {
267
+ "id": window_id,
268
+ "title": title,
269
+ "app": owner,
270
+ "desktop": desktop,
271
+ "visible": True # wmctrl -l only shows visible windows
272
+ }
273
+
274
+ windows.append(window_info)
275
+
276
+ # Sort windows by application name and then by window title
277
+ windows.sort(key=lambda w: (w.get("app", "").lower(), w.get("title", "").lower()))
278
+ except Exception as e:
279
+ print(f"Error getting windows with wmctrl: {str(e)}")
280
+
281
+ # If wmctrl failed, try using xwininfo and xprop
282
+ if not windows:
283
+ try:
284
+ # Get the list of window IDs
285
+ xwininfo_process = subprocess.run(["xwininfo", "-root", "-children"], capture_output=True, text=True)
286
+
287
+ if xwininfo_process.returncode == 0:
288
+ lines = xwininfo_process.stdout.strip().split('\n')
289
+
290
+ # Parse the output to find window IDs
291
+ window_ids = []
292
+ for line in lines:
293
+ # Look for lines with window IDs in hexadecimal format
294
+ if "0x" in line and "child" in line.lower():
295
+ parts = line.split()
296
+ for part in parts:
297
+ if part.startswith("0x"):
298
+ window_ids.append(part)
299
+ break
300
+
301
+ # Get information for each window
302
+ for window_id in window_ids:
303
+ # Get window name
304
+ xprop_name_process = subprocess.run(["xprop", "-id", window_id, "WM_NAME"], capture_output=True, text=True)
305
+
306
+ # Get window class (application)
307
+ xprop_class_process = subprocess.run(["xprop", "-id", window_id, "WM_CLASS"], capture_output=True, text=True)
308
+
309
+ # Extract the window title
310
+ title = ""
311
+ if xprop_name_process.returncode == 0:
312
+ output = xprop_name_process.stdout.strip()
313
+ if "=" in output:
314
+ title = output.split("=", 1)[1].strip().strip('"')
315
+
316
+ # Extract the application name
317
+ app_name = ""
318
+ if xprop_class_process.returncode == 0:
319
+ output = xprop_class_process.stdout.strip()
320
+ if "=" in output:
321
+ classes = output.split("=", 1)[1].strip().strip('"').split('", "')
322
+ app_name = classes[-1] if classes else ""
323
+
324
+ # Create the window info dictionary
325
+ window_info = {
326
+ "id": window_id,
327
+ "title": title,
328
+ "app": app_name,
329
+ "visible": True # Assuming all retrieved windows are visible
330
+ }
331
+
332
+ windows.append(window_info)
333
+
334
+ # Sort windows by application name and then by window title
335
+ windows.sort(key=lambda w: (w.get("app", "").lower(), w.get("title", "").lower()))
336
+ except Exception as e:
337
+ print(f"Error getting windows with xwininfo/xprop: {str(e)}")
338
+
339
+ return windows
340
+
341
+
342
+ def get_available_windows() -> Dict[str, Any]:
343
+ """
344
+ Get detailed information about all available windows currently displayed on screen.
345
+
346
+ Returns:
347
+ Dictionary with platform, success status, and list of windows
348
+ """
349
+ system_name = platform.system().lower()
350
+
351
+ # Get windows based on platform
352
+ if system_name == "darwin" or system_name == "macos":
353
+ windows = _get_windows_macos()
354
+ elif system_name == "windows":
355
+ windows = _get_windows_windows()
356
+ elif system_name == "linux":
357
+ windows = _get_windows_linux()
358
+ else:
359
+ return {
360
+ "success": False,
361
+ "platform": system_name,
362
+ "error": f"Unsupported platform: {system_name}. This tool currently supports macOS, Windows, and Linux.",
363
+ "windows": []
364
+ }
365
+
366
+ # If no windows were found, provide a descriptive error message
367
+ if not windows:
368
+ error_message = "No windows could be detected on your screen. "
369
+ if system_name == "darwin":
370
+ error_message += "This might be due to missing screen recording permissions. Please check System Settings > Privacy & Security > Screen Recording."
371
+ elif system_name == "windows":
372
+ error_message += "This might be due to insufficient permissions or no windows being displayed."
373
+ elif system_name == "linux":
374
+ error_message += "This might be due to wmctrl or xwininfo not being installed or no windows being displayed."
375
+
376
+ return {
377
+ "success": False,
378
+ "platform": system_name,
379
+ "error": error_message,
380
+ "windows": []
381
+ }
382
+
383
+ return {
384
+ "success": True,
385
+ "platform": system_name,
386
+ "count": len(windows),
387
+ "windows": windows
388
+ }
389
+
390
+
391
+ async def handle_get_available_windows(arguments: dict) -> List[types.TextContent]:
392
+ """Handle getting available windows."""
393
+ result = get_available_windows()
394
+
395
+ return [types.TextContent(type="text", text=json.dumps(result, indent=2))]