screenhand 0.4.8 → 0.5.0

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.
@@ -531,17 +531,24 @@ const leaseManager = new LeaseManager(LOCK_DIR);
531
531
  // Playbooks dir holds only executable step sequences for job_create
532
532
  // Resolution order: local dev paths → npm dist paths → ~/.screenhand/ user paths
533
533
  function resolveDataDir(name) {
534
- // 1. Local dev path (when running from source)
534
+ const hasJson = (dir) => fs.existsSync(dir) && fs.readdirSync(dir).some(f => f.endsWith(".json"));
535
+ // 1. Local dev path (when running from source: references/, playbooks/)
535
536
  const local = path.resolve(__dirname, name);
536
- if (fs.existsSync(local) && fs.readdirSync(local).some(f => f.endsWith(".json"))) {
537
+ if (hasJson(local))
537
538
  return local;
538
- }
539
- // 2. npm dist path (when installed via npx/npm)
539
+ // 2. npm dist path — same level (dist-references/ next to dist/)
540
540
  const dist = path.resolve(__dirname, `dist-${name}`);
541
- if (fs.existsSync(dist) && fs.readdirSync(dist).some(f => f.endsWith(".json"))) {
541
+ if (hasJson(dist))
542
542
  return dist;
543
- }
544
- // 3. User home path (always available for user-generated content)
543
+ // 3. npm dist path — parent level (when __dirname is dist/, check ../dist-references/)
544
+ const parentDist = path.resolve(__dirname, "..", `dist-${name}`);
545
+ if (hasJson(parentDist))
546
+ return parentDist;
547
+ // 4. Parent level plain name (../references/)
548
+ const parentLocal = path.resolve(__dirname, "..", name);
549
+ if (hasJson(parentLocal))
550
+ return parentLocal;
551
+ // 5. User home path (always available for user-generated content)
545
552
  const userDir = path.join(os.homedir(), ".screenhand", name);
546
553
  if (!fs.existsSync(userDir)) {
547
554
  fs.mkdirSync(userDir, { recursive: true });
@@ -563,6 +570,9 @@ const seedAppMapsDir = (() => {
563
570
  const dist = path.resolve(__dirname, "dist-app-maps");
564
571
  if (fs.existsSync(dist))
565
572
  return dist;
573
+ const parentDist = path.resolve(__dirname, "..", "dist-app-maps");
574
+ if (fs.existsSync(parentDist))
575
+ return parentDist;
566
576
  const local = path.resolve(__dirname, "seed-app-maps");
567
577
  if (fs.existsSync(local))
568
578
  return local;
@@ -190,6 +190,9 @@ export class CoverageAuditor {
190
190
  }
191
191
  loadReferences(bundleId) {
192
192
  const refs = [];
193
+ // Derive short name for matching: "com.apple.iphonesimulator" → "iphonesimulator"
194
+ const bundleParts = bundleId.split(".");
195
+ const shortName = (bundleParts[bundleParts.length - 1] ?? "").toLowerCase();
193
196
  try {
194
197
  const files = fs.readdirSync(this.referencesDir);
195
198
  for (const file of files) {
@@ -198,7 +201,10 @@ export class CoverageAuditor {
198
201
  try {
199
202
  const raw = fs.readFileSync(path.join(this.referencesDir, file), "utf-8");
200
203
  const ref = JSON.parse(raw);
201
- if (ref.bundleId === bundleId || ref.platform === bundleId) {
204
+ if (ref.bundleId === bundleId ||
205
+ ref.platform === bundleId ||
206
+ ref.platform?.toLowerCase() === shortName ||
207
+ ref.id?.toLowerCase() === shortName) {
202
208
  refs.push(ref);
203
209
  }
204
210
  }
@@ -214,6 +220,27 @@ export class CoverageAuditor {
214
220
  const bundleParts = bundleId.split(".");
215
221
  const shortName = (bundleParts[bundleParts.length - 1] ?? "").toLowerCase();
216
222
  const appNameLower = appName.toLowerCase();
223
+ // Also match by filename prefix (e.g. "simulator-launch-app.json" matches appName "Simulator")
224
+ const filenamePrefixes = [shortName, appNameLower];
225
+ // Find the platform name from reference files for cross-matching
226
+ // e.g. bundleId "com.apple.iphonesimulator" → platform "simulator" in reference
227
+ const refPlatforms = [];
228
+ try {
229
+ const refFiles = fs.readdirSync(this.referencesDir);
230
+ for (const file of refFiles) {
231
+ if (!file.endsWith(".json"))
232
+ continue;
233
+ try {
234
+ const raw = fs.readFileSync(path.join(this.referencesDir, file), "utf-8");
235
+ const ref = JSON.parse(raw);
236
+ if (ref.bundleId === bundleId && ref.platform) {
237
+ refPlatforms.push(ref.platform.toLowerCase());
238
+ }
239
+ }
240
+ catch { /* skip */ }
241
+ }
242
+ }
243
+ catch { /* dir not found */ }
217
244
  try {
218
245
  const files = fs.readdirSync(this.playbooksDir);
219
246
  for (const file of files) {
@@ -222,12 +249,16 @@ export class CoverageAuditor {
222
249
  try {
223
250
  const raw = fs.readFileSync(path.join(this.playbooksDir, file), "utf-8");
224
251
  const pb = JSON.parse(raw);
225
- // Match by bundleId (exact), platform name (case-insensitive), or app name
252
+ // Match by bundleId (exact), platform name (case-insensitive), app name, or ref platform
226
253
  const platformLower = (pb.platform ?? "").toLowerCase();
254
+ const fileBase = file.replace(".json", "").toLowerCase();
227
255
  if (pb.bundleId === bundleId ||
228
256
  platformLower === bundleId ||
229
257
  platformLower === shortName ||
230
- platformLower === appNameLower) {
258
+ platformLower === appNameLower ||
259
+ refPlatforms.includes(platformLower) ||
260
+ filenamePrefixes.some(p => fileBase.startsWith(p + "-")) ||
261
+ refPlatforms.some(rp => fileBase.startsWith(rp + "-"))) {
231
262
  playbooks.push(pb);
232
263
  }
233
264
  }
@@ -20,7 +20,7 @@
20
20
  "consistency": 6
21
21
  },
22
22
  "confidence": 0.13111111111111112,
23
- "lastValidated": "2026-03-25T10:15:29.651Z",
23
+ "lastValidated": "2026-03-25T10:26:08.299Z",
24
24
  "mapVersion": 1,
25
25
  "uiArchitecture": {
26
26
  "type": "other",
@@ -2849,7 +2849,7 @@
2849
2849
  "shortcutsUsed": 20,
2850
2850
  "playbooksExported": 0,
2851
2851
  "edgeCasesHandled": 2,
2852
- "lastRecomputed": "2026-03-25T10:15:18.462Z",
2852
+ "lastRecomputed": "2026-03-25T10:26:36.495Z",
2853
2853
  "visibilityConditions": [
2854
2854
  {
2855
2855
  "elementLabel": "iPhone 16 Plus",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "screenhand",
3
- "version": "0.4.8",
3
+ "version": "0.5.0",
4
4
  "mcpName": "io.github.manushi4/screenhand",
5
5
  "description": "Give AI eyes and hands on your desktop. ScreenHand is an open-source MCP server that lets Claude and other AI agents see your screen, click buttons, type text, and control any app on macOS and Windows.",
6
6
  "homepage": "https://screenhand.com",