Wiring 44 Scripts into Raycast
Why Bother Integrating with Raycast
I've got 44 CLI scripts spread across 4 repositories (doctools/devtools/mactools/clashx), covering document processing, developer productivity, system management, and network proxy. They're useful tools — but they share one problem: I can't remember the commands.
python3 ~/Dev/tools/doctools/scripts/document/md2word.py --template standard --input foo.md — who keeps that in their head?
Raycast solves it: ⌘+Space → type a keyword → enter.
Wrapper Architecture
Each Raycast wrapper is a shell script under raycast/commands/:
#!/bin/bash
# @raycast.title MD → Word
# @raycast.description Convert Markdown to a Word document
# @raycast.icon 📄
# @raycast.mode compact
python3 ~/Dev/tools/doctools/scripts/document/md2word.py "$@"
Key design choices:
- Metadata-driven: title, description, icon all live in comments — Raycast parses them automatically
- Mode selection:
compact(silent execution) vsfullOutput(display output) vsinline(show in the search bar) - Argument passing: declared via
@raycast.argument
Wrapper Distribution Across 4 Repos
| Repository | Scripts | Wrappers | Typical Functions |
|---|---|---|---|
| doctools | 17 | 27 | MD→Word, PDF merge, Excel processing |
| devtools | 10 | 12 | repo management, CF API, health checks |
| mactools | 11 | 13 | file organization, window management, system settings |
| clashx | 6 | 6 | proxy switching, node speed test |
Why are there more wrappers than scripts? Because some scripts have multiple invocation modes, and splitting them into separate wrappers is more intuitive. For example, cf_api.py is split into "DNS list," "DNS add," and "Access list."
Pitfalls Along the Way
1. PATH issues
Raycast's shell environment doesn't load .zshrc, so python3, pnpm, etc. can't be found. Fix: source it manually at the top of each wrapper:
source ~/.zshrc 2>/dev/null
2. Working directory
Raycast executes in / by default, which breaks every relative path inside a script. Fix: each wrapper cds to the right directory first.
3. Interactive input
Raycast doesn't support stdin interaction. Anything that needs input has to be rewritten to take arguments or environment variables.
4. Notification feedback
The user can't see terminal output, so success/failure feedback is invisible. Fix: emit notifications via osascript:
osascript -e 'display notification "Conversion complete" with title "doctools"'
Efficiency Gains
I quantified the before/after on 10 frequent operations (averaged):
- Time per action: dropped from 12 seconds (open terminal → type command → enter) to 3 seconds (⌘+Space → keyword → enter)
- Memory load: shifted from "remember command path and arguments" to "remember a function keyword"
- Error rate: typo-driven retries dropped from 15% to nearly zero
Next Steps
- Wrap commonly used Claude Code skills as Raycast wrappers
- Add parameter forms (Raycast Script Commands support multi-input fields)
- Consider migrating to Raycast Extensions (TypeScript, richer UI)