Creating Pipelines
This guide walks you through creating pipelines to automate tasks with your voice.
Creating Your First Pipeline
Section titled “Creating Your First Pipeline”1. Open Pipeline Settings
Section titled “1. Open Pipeline Settings”- Open Freeway Preferences
- Go to the Pipelines tab
- Click Add First Pipeline (or Add Pipeline if you have existing ones)
2. Configure the Pipeline
Section titled “2. Configure the Pipeline”Fill in the required fields:
- Name: A descriptive name (e.g., “Uppercase Converter”)
- Trigger Pattern: The text pattern to match (e.g., “make uppercase”)
- Match Type: How to match the pattern (e.g., “Starts with”)
- Shell Command: The command to run
3. Test with “Try It”
Section titled “3. Test with “Try It””Click the Try it button to test your pipeline:
- Enter sample input text
- Click Run
- View the output (or error)
4. Save
Section titled “4. Save”Click Save to activate your pipeline.
Pipeline Fields
Section titled “Pipeline Fields”A descriptive name for your pipeline. This appears in the pipeline list and in status messages.
Trigger Pattern
Section titled “Trigger Pattern”The text pattern that activates this pipeline. Combined with the match type, this determines when your pipeline runs.
Tips:
- Keep patterns short and distinct
- Avoid patterns that overlap with normal speech
- Test patterns to ensure they don’t trigger accidentally
Match Type
Section titled “Match Type”Choose how the pattern is matched:
| Match Type | Best For |
|---|---|
| Starts with | Commands like “open…”, “search for…” |
| Ends with | Suffixes like “…please”, “…now” |
| Exact match | Specific phrases like “toggle dark mode” |
| Contains | Keywords that can appear anywhere |
| Regex | Complex patterns with captures |
Shell Command
Section titled “Shell Command”The shell command to execute. This can be:
- A simple command:
date - A pipeline:
tr '[:lower:]' '[:upper:]' - A script:
python3 /path/to/script.py - Multiple commands:
echo "hello" && say "hello"
Writing Shell Commands
Section titled “Writing Shell Commands”Receiving Input
Section titled “Receiving Input”Your command receives the transcribed text via:
# stdin - pipe-friendlycat | your-command
# Environment variable - use in scriptsecho "$FREEWAY_TEXT"Producing Output
Section titled “Producing Output”| Output | Result |
|---|---|
| Non-empty stdout | Replaces the transcribed text (pasted) |
| Empty stdout | Skip paste (nothing is pasted) |
| stderr | Shown as error message |
| Non-zero exit code | Treated as error |
Examples
Section titled “Examples”Transform text (output replaces input):
# Uppercasetr '[:lower:]' '[:upper:]'
# Lowercasetr '[:upper:]' '[:lower:]'
# Reverse linestac
# Sort linessortAction only (empty output skips paste):
# Copy to clipboardpbcopy
# Open URLopen "https://google.com"
# Play soundafplay /System/Library/Sounds/Glass.aiff
# Show notificationosascript -e 'display notification "Done!" with title "Freeway"'Both action and output:
# Copy to clipboard AND pastetee >(pbcopy)
# Log AND outputtee -a /tmp/freeway.logMatch Type Details
Section titled “Match Type Details”Starts With
Section titled “Starts With”Matches if the transcribed text begins with the pattern.
Pattern: open
Matches: “open terminal”, “open finder”, “open the door”
Doesn’t match: “please open”, “reopen”
# Extract what to openopen_what=$(echo "$FREEWAY_TEXT" | sed 's/^open //')open "$open_what"Ends With
Section titled “Ends With”Matches if the transcribed text ends with the pattern.
Pattern: please
Matches: “help me please”, “do it please”
Doesn’t match: “please help me”
Exact Match
Section titled “Exact Match”Matches if the transcribed text exactly equals the pattern (case-insensitive).
Pattern: toggle dark mode
Matches: “toggle dark mode”, “Toggle Dark Mode”
Doesn’t match: “toggle the dark mode”, “dark mode toggle”
Contains
Section titled “Contains”Matches if the pattern appears anywhere in the text.
Pattern: weather
Matches: “what’s the weather”, “weather forecast”, “check weather please”
Matches using a regular expression (ICU syntax). Matching is case-insensitive. Text is normalized before matching: punctuation removed, whitespace trimmed.
Common patterns:
| Pattern | Matches |
|---|---|
^hey freeway | Text starting with “hey freeway” |
please$ | Text ending with “please” |
^(open|start) chat | ”open chat” or “start chat” at the beginning |
^(open|launch|start) | Text starting with “open”, “launch”, or “start” |
send (email|message) to | ”send email to…” or “send message to…” |
remind me in \d+ minutes | ”remind me in 5 minutes”, “remind me in 30 minutes” |
^translate .+ to (spanish|french|german)$ | ”translate hello to spanish” |
^(what|who|where|when|why|how)\b | Questions starting with interrogative words |
^search (for|on )? | ”search cats”, “search for cats”, “search on google” |
Example — search command:
Pattern: ^search for (.+)$
Matches: “search for cats” → captures “cats”
# Using regex in shellif [[ "$FREEWAY_TEXT" =~ ^search\ for\ (.+)$ ]]; then query="${BASH_REMATCH[1]}" open "https://google.com/search?q=$(echo "$query" | sed 's/ /+/g')"fiExample — multi-word trigger:
Pattern: ^(hey|ok|hi) freeway
Matches: “hey freeway do something”, “ok freeway search”
# Strip trigger phrase and process the restcommand=$(echo "$FREEWAY_TEXT" | sed -E 's/^(hey|ok|hi) freeway[,]? *//')echo "Command: $command"Example — optional words:
Pattern: ^open (the )?
Matches: “open terminal”, “open the finder”
# Extract app name, handling optional "the"app=$(echo "$FREEWAY_TEXT" | sed -E 's/^open (the )?//')open -a "$app"Using Scripts
Section titled “Using Scripts”For complex logic, call external scripts:
Python Script
Section titled “Python Script”python3 /path/to/script.py#!/usr/bin/env python3import sys
text = sys.stdin.read()# Process text...print(text.upper())Bash Script
Section titled “Bash Script”/path/to/script.sh#!/bin/bashread text# Process text...echo "${text^^}" # Uppercase in bash 4+AppleScript
Section titled “AppleScript”osascript /path/to/script.scptOr inline:
osascript -e 'tell application "Finder" to make new folder'Timeout
Section titled “Timeout”Pipelines have a 30 second timeout. If your command takes longer, it will be terminated.
For long-running tasks:
- Run them in the background:
your-command & - Use
disownornohup - Start a detached process
# Run in background, no output (skip paste)nohup long-running-task &>/dev/null &Debugging
Section titled “Debugging”Enable Logging
Section titled “Enable Logging”Add logging to your shell command:
echo "Input: $FREEWAY_TEXT" >> /tmp/pipeline-debug.logyour-command | tee -a /tmp/pipeline-debug.logCheck Exit Codes
Section titled “Check Exit Codes”Test commands in Terminal first:
echo "test input" | your-commandecho $? # Should be 0 for successView Freeway Logs
Section titled “View Freeway Logs”Errors appear in:
- Status message in Freeway visualizer
- Freeway debug console (menu bar → Debug)
Best Practices
Section titled “Best Practices”- Test thoroughly — Use “Try it” before relying on a pipeline
- Keep patterns distinct — Avoid overlapping triggers
- Handle errors — Check for empty input, missing dependencies
- Use absolute paths — Don’t rely on PATH for scripts
- Avoid slow commands — Keep execution under a few seconds
- Order matters — Put specific patterns before general ones