From skillkit
Generates Raycast script commands and extensions from natural language tasks. Analyzes complexity to choose type, scaffolds projects, generates code, installs deps, and launches dev servers.
npx claudepluginhub sacredvoid/skillkit --plugin skillkitThis skill uses the workspace's default tool permissions.
Build Raycast extensions and script commands from natural language task descriptions. Handles the full lifecycle: analysis, code generation, setup, and dev launch.
Builds Raycast extensions with React and TypeScript. Provides project structure, package.json templates, command modes (view, no-view, menu-bar), hotkeys, tsconfig, ESLint, and example commands.
Documents Raycast extension patterns: project structure, package.json manifest, React components/hooks with @raycast/api, AI tools, dev workflow, and store guidelines.
Guides creation of slash commands in Claude Code: YAML frontmatter, dynamic arguments, bash execution, file references, organization, and interaction patterns like AskUserQuestion.
Share bugs, ideas, or general feedback.
Build Raycast extensions and script commands from natural language task descriptions. Handles the full lifecycle: analysis, code generation, setup, and dev launch.
All output goes to ~/Documents/raycast-scripts/:
my-script.sh)my-extension/src/index.tsx)Create the directory if it doesn't exist. For extensions, create the subdirectory named after the extension.
User describes a task
│
├─ Analyze complexity
│ ├─ Needs rich UI (lists, forms, grids, detail views)? → Extension
│ ├─ Needs OAuth or token management? → Extension
│ ├─ Needs persistent state across runs? → Extension
│ ├─ Needs menu bar presence? → Extension
│ ├─ Needs pagination or streaming data? → Extension
│ ├─ Simple system operation (open, toggle, copy)? → Script Command
│ ├─ Opens URL or does clipboard work? → Script Command
│ ├─ Runs a shell command or API call with simple output? → Script Command
│ └─ 1-3 simple text inputs, no interactive UI? → Script Command
│
├─ Present recommendation with reasoning to user
├─ User confirms or overrides
│
├─ IF Script Command:
│ ├─ Pick language:
│ │ ├─ macOS + simple task → Bash
│ │ ├─ Windows + simple task → Batch/PowerShell
│ │ ├─ Complex logic / API calls → Python
│ │ └─ Mac-native automation → AppleScript
│ ├─ Generate script with full metadata block
│ ├─ chmod +x the file
│ ├─ Ensure ~/Documents/raycast-scripts/ is created
│ └─ Tell user to add script directory in Raycast if first time
│
└─ IF Extension:
├─ Scaffold project structure
├─ Generate package.json with full manifest
├─ Generate src/ entry point(s)
├─ Run npm install
├─ Run npm run dev
└─ Confirm extension appears in Raycast
Read the reference files in reference/ directory of this skill:
reference/extension-api.md - for extensions (manifest, UI components, hooks, APIs)reference/script-commands.md - for script commands (metadata, templates, examples)reference/patterns.md - for best practices and common patternsCheck platform: Use uname to detect macOS vs other. This affects:
#!/bin/bash
# Required parameters:
# @raycast.schemaVersion 1
# @raycast.title <Title Case Name>
# @raycast.mode <silent|compact|fullOutput|inline>
# Optional parameters:
# @raycast.packageName <Category>
# @raycast.icon <emoji or path>
# @raycast.description <What it does>
# @raycast.needsConfirmation <true for destructive ops>
# @raycast.argument1 { "type": "text", "placeholder": "..." }
| Mode | When to use |
|---|---|
silent | Opens URL, clipboard ops, launches app, background task |
compact | Short confirmation message, quick status |
fullOutput | Multi-line output, formatted results, logs |
inline | Live status display (requires refreshTime, min 10s) |
| Language | Shebang | Extension |
|---|---|---|
| Bash | #!/bin/bash | .sh |
| Python 3 | #!/usr/bin/env python3 | .py |
| Node.js | #!/usr/bin/env node | .js |
| AppleScript | #!/usr/bin/osascript | .applescript |
| Ruby | #!/usr/bin/env ruby | .rb |
| Swift | #!/usr/bin/swift | .swift |
| Language | Arg 1 | Arg 2 | Arg 3 |
|---|---|---|---|
| Bash | $1 | $2 | $3 |
| Python | sys.argv[1] | sys.argv[2] | sys.argv[3] |
| Node.js | process.argv[2] | process.argv[3] | process.argv[4] |
| AppleScript | item 1 of argv | item 2 of argv | item 3 of argv |
| Ruby | ARGV[0] | ARGV[1] | ARGV[2] |
| Swift | CommandLine.arguments[1] | CommandLine.arguments[2] | CommandLine.arguments[3] |
percentEncoded: true for URL argumentsneedsConfirmation: true for destructive operations#!/bin/bash -l if login shell neededinline mode REQUIRES refreshTime (min 10s)extension-name/
├── package.json # Manifest (see reference/extension-api.md)
├── tsconfig.json # TypeScript config
├── src/
│ └── index.tsx # Main command entry point
└── assets/
└── icon.png # 512x512 PNG (or use command-icon.png)
{
"$schema": "https://www.raycast.com/schemas/extension.json",
"name": "extension-name",
"title": "Extension Title",
"description": "What it does",
"icon": "command-icon.png",
"author": "author-name",
"categories": ["Productivity"],
"license": "MIT",
"platforms": ["macOS"],
"commands": [
{
"name": "index",
"title": "Command Title",
"description": "What the command does",
"mode": "view"
}
],
"dependencies": {
"@raycast/api": "^1.98.0",
"@raycast/utils": "^1.19.0"
},
"devDependencies": {
"@raycast/eslint-config": "^1.0.11",
"typescript": "^5.4.5",
"@types/node": "22.14.0",
"@types/react": "19.0.0",
"eslint": "^8.57.0"
},
"scripts": {
"build": "ray build",
"dev": "ray dev",
"fix-lint": "ray lint --fix",
"lint": "ray lint",
"prepublishOnly": "echo \"Error: no publish script\" && exit 1",
"publish": "npx @raycast/api@latest publish"
}
}
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"lib": ["ES2023"],
"module": "Node16",
"moduleResolution": "node16",
"target": "ES2022",
"resolveJsonModule": true,
"strict": true,
"jsx": "react-jsx"
},
"include": ["src/**/*", "env.d.ts", "raycast-env.d.ts"]
}
| Mode | Export | Use case |
|---|---|---|
view | React component (default export) | Interactive UI with List/Detail/Form/Grid |
no-view | Async function (default export) | Background tasks, clipboard ops, no UI |
menu-bar | MenuBarExtra component | Persistent menu bar item (macOS only) |
| Component | When to use |
|---|---|
List | Searchable items, most common. Use isShowingDetail for split view |
Detail | Single item display with markdown + metadata sidebar |
Form | User input collection. Use useForm hook for validation |
Grid | Image-heavy content (icons, thumbnails, galleries) |
MenuBarExtra | Always-visible menu bar status/actions |
| Hook | When to use |
|---|---|
useFetch | HTTP requests with caching |
usePromise | Any async operation |
useCachedPromise | Async with stale-while-revalidate caching |
useExec | Shell command execution |
useForm | Form state + validation |
useLocalStorage | Persistent key-value state |
useCachedState | State that persists across command runs |
useAI | Raycast AI integration (requires Pro) |
useSQL | SQLite database queries |
isLoading prop on top-level components during async opsshowToast(Toast.Style.Failure) for errors, not console.errorshowFailureToast from @raycast/utils for standardized errorsAction.Push for navigation, not imperative useNavigationList.EmptyView / Grid.EmptyView for zero-stateonBlur, clear errors on onChangefiltering={true} when you have onSearchTextChangeenvironment.canAccess() before using Pro features (AI, WindowManagement)~/Documents/raycast-scripts/chmod +x on the file~/Documents/raycast-scripts/@raycast.title~/Documents/raycast-scripts/<extension-name>/npm install in the project directorynpm run dev to start dev modenpm run publishicon@dark.png suffixcommand-icon.png in assets/ as fallbackUse exactly one of: Applications, Communication, Data, Design Tools, Developer Tools, Documentation, Finance, Fun, Media, News, Other, Productivity, Security, System, Web
import { List } from "@raycast/api";
import { useFetch } from "@raycast/utils";
export default function Command() {
const { data, isLoading } = useFetch<Result[]>("https://api.example.com/items");
return (
<List isLoading={isLoading}>
{data?.map((item) => (
<List.Item key={item.id} title={item.name} />
))}
</List>
);
}
import { showToast, Toast, Clipboard } from "@raycast/api";
export default async function Command() {
const text = await Clipboard.readText();
if (!text) {
await showToast(Toast.Style.Failure, "Clipboard is empty");
return;
}
const result = transform(text);
await Clipboard.copy(result);
await showToast(Toast.Style.Success, "Copied to clipboard");
}
import { Form, ActionPanel, Action, showToast, Toast } from "@raycast/api";
import { useForm, FormValidation } from "@raycast/utils";
interface FormValues {
name: string;
description: string;
}
export default function Command() {
const { handleSubmit, itemProps } = useForm<FormValues>({
onSubmit(values) {
showToast(Toast.Style.Success, "Submitted", values.name);
},
validation: {
name: FormValidation.Required,
},
});
return (
<Form actions={<ActionPanel><Action.SubmitForm onSubmit={handleSubmit} /></ActionPanel>}>
<Form.TextField title="Name" placeholder="Enter name" {...itemProps.name} />
<Form.TextArea title="Description" placeholder="Enter description" {...itemProps.description} />
</Form>
);
}
#!/bin/bash
# @raycast.schemaVersion 1
# @raycast.title Search Google
# @raycast.mode silent
# @raycast.icon 🔍
# @raycast.packageName Web Searches
# @raycast.argument1 { "type": "text", "placeholder": "query", "percentEncoded": true }
open "https://www.google.com/search?q=$1"