Script Kit SDK

repository·main·Indexed 26 days ago

https://github.com/johnlindquist/kit

A development kit and SDK for building and running automation scripts. It provides a comprehensive set of APIs for UI components (forms, editors, grids), system integration (clipboard, keyboard, mouse), notifications, media capture, file system management, and development tools like shell execution and Git operations. Includes a robust logging system with correlation IDs and performance timers, as well as safe .env file operation utilities.

Tokens
56.6K
Snippets
206
Records
321
Agent score
80%

What's inside @johnlindquist/kit

  1. Use the Mouse API for cursor control

    main
    The Mouse API provides programmatic control over the system mouse cursor, including movement, clicking, and position retrieval. It uses native system APIs via @jitsi/robotjs to perform these operations across macOS, Windows, and Linux. The coordinate system uses pixels with the origin (0, 0) at the top-left corner of the primary screen. In multi-monitor setups, coordinates extend across all monitors.
  2. Explore Script Kit API Orientation Documents

    main

    The orientation documents provide a deep dive into how Script Kit APIs work, covering everything from SDK implementation to system integration. Use these documents to understand API signatures, implementation flows, platform-specific behaviors (macOS, Windows, Linux), and best practices.

    Each document is structured to include:

    • Overview: Purpose and use cases.
    • API Signature: Parameters and return types.
    • Implementation Flow: Data flow from SDK to App to System.
    • Platform-Specific Behavior: OS differences.
    • Usage Examples: Practical code snippets.
    • Best Practices: Recommended patterns.
  3. Understand the Script Kit SDK Script Discovery Pipeline

    main

    The SDK follows a specific multi-layered pipeline to discover, parse, and manage scripts across different environments (kenvs). The flow of execution is:

    1. getScripts() initiates the process.
    2. getScriptsDb() retrieves existing data from the database.
    3. parseScripts() triggers the parsing logic.
    4. getScriptFiles() performs the file system scan.
    5. parseScript() extracts metadata and analyzes individual files.

    Additionally, the pipeline includes sub-processes for parseScriptlets() and parseSnippets().

  4. Use the Keyboard API for automation

    main

    The keyboard API provides programmatic control over keyboard input, allowing scripts to type text, press key combinations, and simulate keyboard shortcuts. It uses robotjs under the hood for cross-platform automation.

    Important Considerations:

    • Focus Required: The API types into whatever application currently has focus. Scripts must ensure the correct window or field is focused before calling keyboard methods.
    • Real Keyboard Events: Actions are indistinguishable from physical keyboard input.
    • Security: Be cautious when typing sensitive data like passwords.
  5. Implement task progress patterns with setStatus

    main

    When performing long-running or multi-step operations, use setStatus to provide granular feedback.

    Best Practices:

    • Use try/finally: Always wrap operations in a try/finally block to ensure the status is reset to default even if the operation fails.
    • Update incrementally: For loops or progress events (like downloads), update the message with current progress (e.g., Processing 5/10).
    • Avoid spam: While there is no rate limiting, updating too frequently can be unnecessary overhead.
    async function processFiles(files: string[]) {
      await setStatus({
        status: 'busy',
        message: `Processing ${files.length} files...`
      })
      
      try {
        for (let i = 0; i < files.length; i++) {
          await processFile(files[i])
          await setStatus({
            status: 'busy',
            message: `Processing file ${i + 1}/${files.length}`
          })
        }
        
        await setStatus({
          status: 'success',
          message: 'All files processed!'
        })
      } catch (error) {
        await setStatus({
          status: 'error',
          message: `Error: ${error.message}`
        })
      }
    }
  6. Choose the right text insertion API

    main

    When automating text entry, choose the method that best fits your performance and compatibility needs:

    • keyboard: Use for cross-platform keyboard automation and simulating actual key presses.
    • setSelectedText: Use when you simply need to insert text at the cursor position (faster than typing).
    • clipboard: Use for data transfer without direct UI interaction.
    • Legacy APIs (keystroke, pressKeyboardShortcut): Avoid these unless you are targeting macOS-specific AppleScript scenarios.
  7. Use platform-specific notification features

    main

    The notify() API allows for specialized behavior depending on the operating system:

    macOS

    Supports subtitles, interactive action buttons, inline replies, and custom sounds.

    await notify({
      title: "New Message",
      subtitle: "John Doe",
      body: "Hey, are you free for lunch?",
      hasReply: true,
      replyPlaceholder: "Type your reply...",
      actions: [
        { type: "button", text: "Reply" },
        { type: "button", text: "Ignore" }
      ]
    })

    Windows

    Supports custom layouts via toastXml and persistent notifications using timeoutType: "never".

    await notify({
      title: "Reminder",
      body: "Meeting in 5 minutes",
      timeoutType: "never"
    })

    Linux

    Supports priority hints via the urgency property.

    await notify({
      title: "System Alert",
      body: "Critical update available",
      urgency: "critical"
    })
    // macOS with subtitle and actions
    await notify({
      title: "New Message",
      subtitle: "John Doe",
      body: "Hey, are you free for lunch?",
      hasReply: true,
      replyPlaceholder: "Type your reply...",
      actions: [
        { type: "button", text: "Reply" },
        { type: "button", text: "Ignore" }
      ]
    })
    
    // Windows with custom timeout
    await notify({
      title: "Reminder",
      body: "Meeting in 5 minutes",
      timeoutType: "never"
    })
    
    // Linux with urgency
    await notify({
      title: "System Alert",
      body: "Critical update available",
      urgency: "critical"
    })
  8. Recover lost or corrupted .env files

    main

    If your .env file is lost or corrupted, you can use the built-in recovery tool to interactively list, preview, and restore from available backups.

    CLI Command:

    kit recover-env

    Manual Recovery Steps:

    1. List backups in the .kenv directory: ls ~/.kenv/.env.backup.*
    2. Restore a specific backup manually: cp ~/.kenv/.env.backup.YYYY-MM-DDTHH-MM-SS ~/.kenv/.env

    Emergency Recovery: If no backups exist, recreate the environment from the template:

    kit create-env
  9. Handle errors when trashing files

    main

    When using trash, wrap calls in a try/catch block to handle cases where files might not exist or permissions are denied. You can check the error message to determine if the failure was due to a missing file.

    try {
      await trash("important-file.txt")
    } catch (error) {
      if (error.message.includes("does not exist")) {
        console.log("File already removed")
      } else {
        console.error("Failed to trash file:", error)
      }
    }
  10. Auto-population of fields from command line arguments

    main

    The fields API automatically attempts to populate fields using command line arguments if they are available. If a script is called with arguments, they will be assigned to the fields in order.

    // If script called with: script.js value1 value2
    let [field1, field2] = await fields(["Field 1", "Field 2"])
    // field1 = "value1", field2 = "value2"
  11. Best practices for managing temporary files

    main

    When using tmpPath, follow these best practices to ensure reliability and system health:

    • Clean Up Large Files: Use a try...finally block to ensure that large downloads or heavy processing files are removed using trash() even if the script fails.
    • Use Subdirectories: Organize different types of temporary data (e.g., downloads, cache, processing) into distinct subdirectories to avoid clutter.
    • Add Timestamps: When generating filenames, include a timestamp (e.g., new Date().toISOString()) to prevent filename collisions during rapid execution.