- Pipeline mode: new task mode that chains agentic tasks sequentially with foreach/subdirectory iteration and skip_if conditions - Pipeline executor backend (WebSocket at /api/tasks/pipeline/ws) with support for both Pi and Claude Code models - Frontend PipelineRunner component with step progress, streaming output, and aggregate cost tracking - New agentic tasks: prepare-discography, fetch-album-info, build-discography (pipeline combining both) - Seed parser extended to handle pipeline steps in frontmatter config - CopyButton component added to assistant bubbles, error bubbles, and tool input/output sections - Removed obsolete SearXNG/Apify/browser relay code from pi-bridge Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
6.0 KiB
Script Task Templates
When creating a script-mode task, always use one of these templates. The template handles all boilerplate (input parsing, validation, iteration, error handling, summary). You only write the task-specific logic.
Inputs
All task inputs defined in the TASK.md frontmatter are available to the script in two ways:
- Environment variables:
INPUT_<NAME>(uppercase) — e.g.file_path→$INPUT_FILE_PATH - Positional arguments: if
args: [file_path]is set in TASK.md, the value is passed as$1
Input types and UI rendering
| Type | UI | Notes |
|---|---|---|
string |
Text field | Free-form text input |
string + options |
Selectable pills | User picks one from a list |
number |
Number field | Numeric input |
boolean |
No/Yes toggle | Defaults to false if not specified |
Inputs provided by context (e.g. file_path from the file browser) are auto-filled and hidden from the form.
Autofill
Inputs can declare autofill to pre-fill from the trigger context. Available context values:
| Value | Source |
|---|---|
entry_name |
Name of the file or directory that triggered the task |
entry_path |
Full path of the file or directory |
Autofilled inputs are visible and editable (unlike file_path which is hidden).
Templates
1. File Processor
Use when the task operates on a file or directory of files.
When to use: converting files, transcoding, extracting metadata, batch renaming, any per-file operation.
Structure: Define EXTENSIONS and process_file(), then the template handles discovery, iteration, and summary.
#!/usr/bin/env bash
set -euo pipefail
# ── Task: <Task Name> ──
EXTENSIONS="ext1|ext2|ext3"
process_file() {
local input="$1"
# ... do work on $input ...
# return 0 on success, 1 on failure
}
# ── Template: File Processor ──
TARGET="${1:-${INPUT_FILE_PATH:-}}"
if [[ -z "$TARGET" ]]; then
echo "Error: No file or directory path provided" >&2
exit 1
fi
if [[ ! -e "$TARGET" ]]; then
echo "Error: Path does not exist: $TARGET" >&2
exit 1
fi
converted=0
failed=0
if [[ -f "$TARGET" ]]; then
if process_file "$TARGET"; then
converted=$((converted + 1))
else
failed=$((failed + 1))
fi
elif [[ -d "$TARGET" ]]; then
while IFS= read -r -d '' file; do
if process_file "$file"; then
converted=$((converted + 1))
else
failed=$((failed + 1))
fi
done < <(find "$TARGET" -type f -regextype posix-extended -iregex ".*\.($EXTENSIONS)" -print0 | sort -z)
if [[ $converted -eq 0 && $failed -eq 0 ]]; then
echo "No matching files found in: $TARGET"
exit 0
fi
else
echo "Error: Not a file or directory: $TARGET" >&2
exit 1
fi
echo ""
echo "Summary: $converted processed, $failed failed"
Slots to fill:
EXTENSIONS— pipe-separated list of file extensions to match when processing a directoryprocess_file()— receives one argument: the full path to the file. Print progress to stdout, errors to stderr. Return 0 on success, 1 on failure.
Example (convert audio to MP3):
EXTENSIONS="flac|wav|ogg|wma|aac|m4a|opus"
process_file() {
local input="$1"
local output="${input%.*}.mp3"
if [[ -f "$output" ]]; then
echo "Skipping (exists): $output"
return 0
fi
echo "Converting: $input"
if ffmpeg -i "$input" -codec:a libmp3lame -b:a 320k -y "$output" 2>/dev/null; then
echo " Done"
else
echo " Failed" >&2
return 1
fi
}
2. Standalone
Use when the task doesn't operate on a specific file target — it uses inputs from environment variables or takes no input at all.
When to use: fetching data from APIs, generating reports, system tasks, anything that isn't per-file processing.
#!/usr/bin/env bash
set -euo pipefail
# ── Task: <Task Name> ──
main() {
# Access inputs via $INPUT_* environment variables
# e.g. $INPUT_COUNTRY, $INPUT_LIMIT, $INPUT_OUTPUT_DIR
# ... do work ...
}
# ── Template: Standalone ──
main
Slots to fill:
main()— the entire task logic. Use$INPUT_*env vars for parameters.
Example (download RSS feed):
main() {
local url="$INPUT_FEED_URL"
local output="${INPUT_OUTPUT_DIR:-$HOME}/feed.xml"
echo "Fetching: $url"
if curl -sSL "$url" -o "$output"; then
echo "Saved to: $output"
else
echo "Failed to fetch feed" >&2
exit 1
fi
}
TASK.md Frontmatter
Every script task needs a TASK.md with this structure:
---
name: Task Name
description: What the task does
version: 1
mode: script
language: bash
triggers: # optional — enables context menu on matching files
- type: file
extensions:
- ext1
- ext2
- type: directory
inputs:
input_name:
type: string # string, number, boolean
description: What this input is
default: value # optional — default value
autofill: entry_name # optional — pre-fill from context (entry_name or entry_path)
options: # optional — renders as selectable pills in UI
- option1
- option2
args: [input_name] # optional — pass inputs as positional args
---
# Task Name
Brief description of what the task does.
Rules
- Always use
set -euo pipefail— fail fast on errors. - Print progress to stdout — the user sees this streamed in the UI.
- Print errors to stderr — they appear in red in the UI.
- Return proper exit codes — 0 = success, non-zero = failure. The UI shows "Task complete" or "Task failed" based on exit code.
- Don't assume tools are installed — check with
command -vbefore using optional dependencies. - Use
$INPUT_*env vars — never hardcode paths or values that should come from inputs. - The script runs inside a bwrap sandbox for non-admin users — it has access to
/data/home/(user's home),/tmp, and standard system tools. It cannot access other users' data. - Keep scripts self-contained — the entire script is stored in the database as a single text blob. No external file dependencies.