Claude
Skills
Sign in
Back

raycast-alfred

Included with Lifetime
$97 forever

macOS launcher automation with Raycast extensions (TypeScript/React) and Alfred workflows (AppleScript/Python) for keyboard-driven productivity

devtoolsraycastalfredmacoslauncherautomationproductivityscriptstypescript

What this skill does


# Raycast & Alfred Skill

Master macOS launcher automation with Raycast extensions and Alfred workflows. This skill covers TypeScript-based Raycast development, AppleScript/Python Alfred workflows, keyboard shortcuts, clipboard management, and productivity automation patterns.

## When to Use This Skill

### USE when:
- Building quick access tools for developer workflows
- Automating repetitive macOS tasks
- Creating custom search commands
- Building clipboard history managers
- Implementing text snippet expansion
- Creating project launchers and switchers
- Building API query tools
- Automating application control
- Creating custom keyboard shortcuts
- Building team productivity tools

### DON'T USE when:
- Cross-platform automation needed (use shell scripts)
- Server-side automation (use cron/systemd)
- GUI testing automation (use Playwright/Selenium)
- Windows/Linux environments
- Heavy computation tasks (use proper CLI tools)

## Prerequisites

### Raycast Setup

```bash
# Install Raycast
brew install --cask raycast

# Install Node.js (required for extension development)
brew install node

# Install Raycast CLI
npm install -g @raycast/api

# Create new extension
npx create-raycast-extension --name my-extension

# Development mode
cd my-extension
npm install
npm run dev

# Extension structure
# my-extension/
# ├── package.json
# ├── tsconfig.json
# ├── src/
# │   ├── index.tsx          # Main command
# │   └── other-command.tsx  # Additional commands
# └── assets/
#     └── icon.png           # Extension icon
```

### Alfred Setup

```bash
# Install Alfred (Powerpack required for workflows)
brew install --cask alfred

# Alfred workflow locations
# ~/Library/Application Support/Alfred/Alfred.alfredpreferences/workflows/

# Create workflow via Alfred Preferences > Workflows > + > Blank Workflow

# Workflow components:
# - Triggers: Keywords, hotkeys, file actions
# - Actions: Scripts, open URL, run NSAppleScript
# - Outputs: Notifications, copy to clipboard, play sound

# Script languages supported:
# - bash, zsh
# - Python (2 or 3)
# - AppleScript / JavaScript for Automation (JXA)
# - Ruby, PHP, Perl
```

### Development Environment

```bash
# For Raycast TypeScript development
npm install -g typescript @types/node

# For Alfred Python workflows
pip install alfred-workflow  # (legacy, but useful patterns)

# AppleScript tools
brew install --cask script-debugger  # Optional: AppleScript IDE

# Testing tools
brew install jq  # JSON parsing
```

## Core Capabilities

### 1. Raycast Script Commands

```bash
#!/bin/bash

# Required parameters:
# @raycast.schemaVersion 1
# @raycast.title Open Project
# @raycast.mode silent

# Optional parameters:
# @raycast.icon 📁
# @raycast.argument1 { "type": "text", "placeholder": "Project name", "optional": false }
# @raycast.packageName Developer Tools

# Documentation:
# @raycast.description Opens a project in VS Code
# @raycast.author Your Name
# @raycast.authorURL https://github.com/yourname

PROJECT="$1"
PROJECT_DIR="$HOME/projects/$PROJECT"

if [ -d "$PROJECT_DIR" ]; then
    code "$PROJECT_DIR"
    echo "Opened $PROJECT"
else
    echo "Project not found: $PROJECT"
    exit 1
fi
```

```bash
#!/bin/bash

# @raycast.schemaVersion 1
# @raycast.title Git Status
# @raycast.mode fullOutput
# @raycast.icon 🔀
# @raycast.packageName Git

# @raycast.description Show git status for current directory
# @raycast.author workspace-hub

cd "$(pwd)" || exit 1

if [ -d ".git" ]; then
    echo "Branch: $(git branch --show-current)"
    echo ""
    echo "Status:"
    git status --short
    echo ""
    echo "Recent commits:"
    git log --oneline -5
else
    echo "Not a git repository"
    exit 1
fi
```

```python
#!/usr/bin/env python3

# Required parameters:
# @raycast.schemaVersion 1
# @raycast.title UUID Generator
# @raycast.mode silent

# Optional parameters:
# @raycast.icon 🔑
# @raycast.argument1 { "type": "dropdown", "placeholder": "Format", "data": [{"title": "Standard", "value": "standard"}, {"title": "No dashes", "value": "nodash"}, {"title": "Uppercase", "value": "upper"}] }
# @raycast.packageName Utilities

import uuid
import subprocess
import sys

format_type = sys.argv[1] if len(sys.argv) > 1 else "standard"

new_uuid = str(uuid.uuid4())

if format_type == "nodash":
    new_uuid = new_uuid.replace("-", "")
elif format_type == "upper":
    new_uuid = new_uuid.upper()

# Copy to clipboard
subprocess.run(["pbcopy"], input=new_uuid.encode(), check=True)

print(f"Copied: {new_uuid}")
```

```bash
#!/bin/bash

# @raycast.schemaVersion 1
# @raycast.title Kill Port
# @raycast.mode compact
# @raycast.icon 🔌
# @raycast.argument1 { "type": "text", "placeholder": "Port number" }
# @raycast.packageName Developer Tools

PORT="$1"

# Find process on port
PID=$(lsof -ti:$PORT 2>/dev/null)

if [ -z "$PID" ]; then
    echo "No process on port $PORT"
    exit 0
fi

# Kill the process
kill -9 $PID 2>/dev/null

if [ $? -eq 0 ]; then
    echo "Killed process $PID on port $PORT"
else
    echo "Failed to kill process on port $PORT"
    exit 1
fi
```

### 2. Raycast TypeScript Extensions

```tsx
// src/index.tsx
// ABOUTME: Raycast extension main command
// ABOUTME: Project launcher with favorites and recent

import {
  ActionPanel,
  Action,
  List,
  Icon,
  LocalStorage,
  showToast,
  Toast,
  getPreferenceValues,
} from "@raycast/api";
import { useState, useEffect } from "react";
import { exec } from "child_process";
import { promisify } from "util";
import fs from "fs";
import path from "path";

const execAsync = promisify(exec);

interface Preferences {
  projectsDir: string;
  editor: string;
}

interface Project {
  name: string;
  path: string;
  lastOpened?: number;
  isFavorite?: boolean;
}

export default function Command() {
  const [projects, setProjects] = useState<Project[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const preferences = getPreferenceValues<Preferences>();

  useEffect(() => {
    loadProjects();
  }, []);

  async function loadProjects() {
    try {
      const projectsDir = preferences.projectsDir.replace("~", process.env.HOME || "");
      const dirs = fs.readdirSync(projectsDir, { withFileTypes: true });

      // Load favorites and recent from storage
      const favoritesJson = await LocalStorage.getItem<string>("favorites");
      const recentJson = await LocalStorage.getItem<string>("recent");

      const favorites = favoritesJson ? JSON.parse(favoritesJson) : [];
      const recent = recentJson ? JSON.parse(recentJson) : {};

      const projectList: Project[] = dirs
        .filter((dir) => dir.isDirectory() && !dir.name.startsWith("."))
        .map((dir) => ({
          name: dir.name,
          path: path.join(projectsDir, dir.name),
          lastOpened: recent[dir.name] || 0,
          isFavorite: favorites.includes(dir.name),
        }))
        .sort((a, b) => {
          // Favorites first, then by recent
          if (a.isFavorite && !b.isFavorite) return -1;
          if (!a.isFavorite && b.isFavorite) return 1;
          return (b.lastOpened || 0) - (a.lastOpened || 0);
        });

      setProjects(projectList);
    } catch (error) {
      showToast({
        style: Toast.Style.Failure,
        title: "Failed to load projects",
        message: String(error),
      });
    } finally {
      setIsLoading(false);
    }
  }

  async function openProject(project: Project) {
    try {
      const editor = preferences.editor || "code";
      await execAsync(`${editor} "${project.path}"`);

      // Update recent
      const recentJson = await LocalStorage.getItem<string>("recent");
      const recent = recentJson ? JSON.parse(recentJson) : {};
      recent[project.name] = Date.now();
      await LocalStorage.setItem("recent", JSON.stringify(recent));

      showToast({
        style: Toast.Style.Success,
        title: `Opened ${project.name}`,
      });
    } catch (error) {
      showToast({
        style: Toast.Style.Failure,
        title: "Failed to open project",

Related in devtools