Landing
<title>Vibe Action</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta property="og:title" content="Vibe Action" />
<meta property="og:description" content="Command router for shell and LLM tasks" />
<link rel="icon" href="assets/images/favicon.png" />
Action
# Quick Start## 1. Install Ollama (or a cloud provider)
## 2. Install Vibe Action cargo install vibe-action
## 3. AI-powered commit vibe-action project commit
## 4. Describe a screenshot vibe-action vision describe
## 5. Translate a file vibe-action text translate README.md
19actions30+operators5LLM providers2IDE pluginsExecutionTurn any input into a running pipeline.01selectionText, file, image or interactive prompt — from the CLI or an IDE.→02shellShell commands run locally with live terminal output.→03llmtiny → vision tiers. Ollama locally, or cloud API. Parallel across the cluster.→04outputResult prints to the terminal, copies to clipboard or fires a notification.Why Vibe ActionCLI-first. No browser, no context switching.01Tag systemSteps connect via tag references. The engine builds a dependency graph and executes in topological order.02Operators30+ operators chained in mods: fetch, text, ast, trim, uniq, join and more. Read, inspect, transform and write.03GuardsConditional execution in pure YAML: when skips, fail bails, reg validates output. No shell scripting.04System tags20+ built-in tags for environment context: user, OS, pwd, date and more.05Vision & FetchDescribe screenshots, read images from URL or clipboard, load web pages and PDFs.06Action cacheStartup loads a validated snapshot instead of re-parsing YAML — the CLI responds instantly.YAML ActionsDescribe actions in YAML, not code.01Write a YAML file with steps, data sources and guards.02Graph. The engine resolves tag references into topological order.03Run. Shell commands locally, LLM prompts on your cluster.04Validate with reg. Replace selection, copy to clipboard, or show a dialog.version: 0.0.2 name: extract about: Extract matching lines from logs api: output: dialog input: query_prompt actions:
- tag: llm_log
run: large
val:
- name: search data: query_prompt
- name: line data: arg_file mods: fetch|text each: true action: Match line against {search}
- tag: out_log
run: value
val:
- name: result data: llm_log mods: split|filter:eq:-|uniq|join action: {result}
DocsDig deeper.
Introduction
Vibe Action is a command router that executes shell commands and LLM prompts defined in simple YAML pipelines.
Why Vibe Action
- ⚡ One command = complex pipeline — chain shell scripts and LLM calls into a single action
- 🔗 Tag system — connect steps via data references with an automatic dependency graph
- 🧩 Operators — pipeline of value transformations in
mods(read, inspect, transform, write) - 🛡️ Guards —
when/failpredicates andregoutput validation, all in YAML - 🖥️ System tags — runtime environment: OS, user, directories, time, and more
- 📥 Unified input — query tags handle text, files, images, and interactive prompts from CLI or IDE
- 🗂️ Action groups — load actions from external git repositories or local directories as nested
vibe-action <group> <action>commands - 👁️ Vision support — screenshot description, person identification, image from URL or clipboard
- 🤖 Batch LLM — parallel execution across cluster nodes with role-based routing (
tiny,small,medium,large,vision) - ⏱️ Process guard — a new run supersedes the previous one, keeping shared state predictable
- 🔔 Notifications — optional desktop notifications on completion
- 🔐 Confirmations — ask before executing dangerous commands
- 💬 Self-documenting — the built-in
faqaction answers questions about Vibe Action itself - 🔌 IDE integration — the
apiblock drives the VS Code and IntelliJ plugins - 🔒 Open & flexible — open source. Local models via Ollama or cloud APIs (DeepSeek, Qwen, Kimi, Zhipu)
- 🦀 Fast — built in Rust
Key Concepts
YAML pipelines
Describe your workflow in YAML, not code. Each file is one CLI command:
version: 0.0.2
name: extract
about: Extract matching lines from text and logs
api:
output: dialog
input: query_prompt
actions:
- tag: llm_log
run: large
reg: '^[^\n]+$'
val:
- name: search
data: query_raw
- name: line
data: arg_file
mods: 'fetch|text'
fail: 'is:empty:not'
each: true
action: |
[Task]
If the line matches the query — output the EXACT line unchanged.
If it does not match — output only a single dash: "-"
[Query]
{search}
[Line]
{line}
Steps resolve their inputs from val candidates, execute in dependency order, and pass results forward by tag. See Pipeline YAML.
Val candidates
Every step declares where its data comes from: a tag, an argument, or a query. Candidates resolve in order — the first one whose guard passes wins. See Val Candidates.
Operators
mods chains operators left to right: fetch|text, split|filter:eq:-|uniq|join. Guards use inspect operators only. See the operator pages: Read, Inspect, Transform, Write.
How It Works
- You write a YAML file describing your workflow — steps, data sources, guards
- The engine loads pipelines from the actions directory and builds a CLI command per file
- Steps execute in dependency order — shell commands run locally, LLM prompts go to your cluster
- Outputs are validated against optional
regpatterns - The final result goes to the terminal, the editor, or the clipboard
Ready to try it? Head to Getting Started.
Getting Started
Prerequisites
- Ollama (recommended), or API keys for DeepSeek, Qwen, Kimi, or Zhipu — for LLM inference
- Rust (if building from source)
Supported Platforms
- macOS — full support
- Linux — full support
Install Ollama
# macOS / Linux
curl -fsSL https://ollama.com/install.sh | sh
# Pull models for different roles
ollama pull qwen2.5-coder:3b-instruct # small
ollama pull qwen2.5-coder:7b-instruct # medium
ollama pull qwen2.5-coder:14b-instruct # large
ollama pull qwen2.5vl:7b # vision
Install Vibe Action
Via Cargo (recommended):
cargo install vibe-action
Build from source:
git clone https://github.com/keygenqt/vibe-action.git
cd vibe-action
cargo build --release
First Run
On first run, Vibe Action creates the config and default actions:
$ vibe-action --help
This creates:
~/.vibe-action/config.yaml— configuration~/.vibe-action/actions/— built-in actions
Configure Cluster
Edit ~/.vibe-action/config.yaml to point to your Ollama instance:
cluster:
- provider: ollama
host: http://localhost:11434
model: qwen2.5-coder:14b-instruct
Full field reference — roles, timeouts, multi-node clusters — see Configuration.
Run Your First Actions
# AI-powered commit
vibe-action commit
# Translate a file
vibe-action translate README.md
# Describe a screenshot
vibe-action describe screenshot.png
Full list of ready-to-use actions — Built-in Actions.
System commands (status, clean, stop) and environment variables —
CLI Reference.
Next Steps
- YAML Format — learn the action manifest schema
- Val Candidates — data binding and guards
- Custom Actions — write your own
- Query Providers — unified input for CLI and IDE
- System Providers — runtime environment tags
Pipeline — YAML Format
Each action is a YAML file in ~/.vibe-action/actions/. The file name
becomes the CLI subcommand. Custom files are loaded automatically
alongside the built-in defaults.
Top-level fields
version: '0.0.2'
name: my-action
about: Short description for CLI help
notify: true
args:
- name: arg_format
short: 'f'
input: string
default: json
help: 'Output format'
api:
output: replace
input: query_raw
args:
arg_format: query_raw
actions:
- tag: tag_result
run: small
val:
- name: content
data: query_raw
action: |
[Task]
Do something with {content}
| Field | Type | Required | Description |
|---|---|---|---|
version | string | yes | Pipeline schema version (must match PIPELINE_VERSION) |
name | string | yes | Action name — becomes CLI subcommand |
about | string | yes | Short description for --help |
notify | bool | no | Desktop notification on completion (default: false) |
check | string | no | Regex to validate final pipeline result; fail if no match |
args | list | no | CLI argument definitions |
api | object | no | IDE plugin metadata (ignored by CLI runtime) |
actions | list | yes | Ordered list of pipeline steps |
Args
Each argument becomes a CLI flag and a val-candidate tag under its own
name. Convention: prefix names with arg_ (e.g. arg_file → flag
--arg_file, tag arg_file).
args:
- name: arg_dry_run
short: 'd'
input: bool
default: false
help: 'Print message without executing'
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Argument name; used verbatim as tag and --<name> flag (convention: arg_ prefix) |
short | char | no | Short flag (single ASCII letter) |
input | string | yes | string, bool, number, path |
default | string | no | Default value; makes the argument optional |
help | string | no | Help text for CLI usage |
API block
Metadata for the IDE plugin: input source, output target, extra args. Ignored by the CLI runtime. Field reference and semantics — see IDE Plugin.
Actions
Each item is one pipeline step. Execution order is determined automatically by data dependencies (topological sort), not by list position.
actions:
- tag: tag_result
run: medium
reg: '.+'
ask: true
when: 'is:empty:not'
val:
- name: content
data: query_raw
mods: 'fetch|text'
when: 'is:path'
action: |
[Task]
Process {content}
| Field | Type | Required | Description |
|---|---|---|---|
tag | string | yes | Unique identifier; referenced by other steps via data |
run | string | yes | Execution engine (see below) |
val | list | no | Val candidates — see Val Candidates |
when | string | no | Action-level guard (inspect operators); skip entire action if false |
reg | string | no | Regex to validate step output; fails if no match |
ask | bool | no | Prompt user confirmation before execution (default: false) |
action | string | yes | Template with {name} placeholders from val candidates |
Action-level when
An action-level when is evaluated before any candidates are resolved.
If it fails, the entire action is skipped (dead tag). Accepts inspect
operators only — the same set as candidate-level when and fail.
This is separate from candidate-level when, which controls individual
candidate selection. Use the action-level guard to skip an entire step
based on runtime conditions.
Run types
| Value | Engine |
|---|---|
value | Literal passthrough — no execution, template as-is |
cmd | Shell command via sh -c; values are shell-quoted |
tiny | LLM prompt — tiny model (1-3b) |
small | LLM prompt — small model (3-7b) |
medium | LLM prompt — medium model (7-14b) |
large | LLM prompt — large model (14b+) |
vision | LLM prompt with extracted base64 images (vision model) |
LLM sizes route to cluster nodes by role — see
Configuration.
Output validation (reg)
If reg is set, the step’s output is tested against the regex. No match
→ hard error, pipeline aborts. Use it to enforce output format:
reg: '.+' # must be non-empty
reg: '^[^\n]+$' # must be a single line
reg: 'DIRTY|CLEAR' # must be one of these words
Pipeline-level check
The top-level check field validates the final pipeline result (the
output of the last action). Same behavior as reg — no match → hard
error. Use it when you need to enforce the overall result format rather
than individual step output.
Reserved prefixes
Action tags and argument names must not use query_* or system_*
prefixes — those are reserved for providers. A bare query tag is also
forbidden; use query_raw instead.
Self-referencing (data points to the action’s own tag) is a
validation error.
Version management
The version field must match the engine’s PIPELINE_VERSION. For
custom actions a mismatch is a validation error — bump version when
the pipeline schema changes.
Built-in files are instead overwritten with the embedded default on version mismatch — see Built-in Actions.
Val Candidates
Val candidates are the data-binding mechanism in each action step. They
resolve source tags into {name} placeholders that the action template
expands.
Schema
val:
- name: content
data: query_raw
when: 'is:path'
mods: 'fetch|text'
- name: content
data: query_raw
fail: 'is:empty:not'
each: true
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Placeholder name used as {name} in action |
data | string | no | Source tag or literal value |
mods | string | no | Operator pipe: op:arg|op:arg|... applied left to right |
when | string | no | Pre-mods soft guard (inspect operators); skip candidate if false |
fail | string | no | Post-mods hard guard (inspect operators); bail if false |
each | bool or object | no | Fan-out config for list data (split/merge) |
Resolution order
Candidates are evaluated in list order. For each unique name, the
first candidate whose when passes (or has none) wins. Subsequent
candidates with the same name are skipped.
If no candidate wins for a name, the name resolves to an empty string — the entire action becomes a dead tag and is skipped by the engine.
Data sources
data references a tag from one of these namespaces:
| Prefix | Source | Example |
|---|---|---|
query_* | Query provider (user input) | query_raw |
system_* | System provider (runtime) | system_language |
arg_* | CLI argument | arg_dry_run |
tag_* | Another action’s result | tag_project_path |
| (none) | Mods-only candidate (no input) | screenshot operator |
When data references another action’s tag, a dependency is created.
The engine resolves actions in topological order — the referenced action
must complete before this one runs.
Guards
when — soft guard (skip)
Evaluated before mods are applied. If the result is falsy, the
candidate is skipped (not an error). Use to avoid unnecessary work,
especially for side-effect operators like screenshot:
val:
- name: content
data: query_raw
when: 'is:path' # only resolve if input is a file path
mods: 'fetch|text'
- name: content
data: query_raw # fallback: use raw text
fail: 'is:empty:not' # hard error if empty
fail — hard guard (bail)
Evaluated after mods are applied. If the result is falsy, the
pipeline aborts with an error. Use to enforce that a value is valid:
val:
- name: content
data: query_raw
fail: 'is:empty:not' # bail if content is empty
Both when and fail accept inspect operators only — see
Operators — Inspect.
Mods
Operator pipe applied left to right. Each operator receives the output
of the previous one. The initial value comes from data.
mods: 'fetch|text' # fetch URL/file, then extract text
mods: 'resolve:dir|scan|ast|format:json' # resolve dir, scan, parse AST, format
mods: 'split|filter:eq:-|uniq|join' # split lines, drop "-", deduplicate, join
Operators are not inline in the action template — they live in
mods. See Read,
Inspect,
Transform, and
Write.
Operator arguments can reference other tags via {name} interpolation
(e.g. mods: 'file:{tag_path}') — see
Placeholders & Escapes.
each — fan-out
When each is set, the resolved value is split into items, and the
action template is expanded once per item. Results are merged back
into a single value.
Boolean form
each: true
Equivalent to split: '\n' / merge: '\n' — splits by newline, merges
with newline.
Object form
each:
split: '\n'
merge: '\x1F'
| Field | Description |
|---|---|
split | Separator to split the value into items |
merge | Separator to join results back |
Both support escape mnemonics (\n, \t, \s, \x1F).
How fan-out works
- Resolve the candidate value (after
mods). - Split the value by
each.split→ list of items. - For each item, substitute
{name}in theactiontemplate. - Execute each expanded action independently.
- Join all results with
each.merge→ single string. - Store under the action’s tag.
Example — process each changed file independently:
val:
- name: file
data: tag_changed_files
mods: 'trim'
each:
split: '\n'
merge: '\x1F'
action: |
cd {path}
git diff HEAD -- {file} 2>/dev/null | head -c 6000
Dead tags
If no candidate wins for any declared name, the entire action
resolves to an empty string. The engine marks it as a dead tag and
skips execution. Downstream actions that reference this tag receive an
empty value.
This is intentional — it allows conditional actions like dry-run guards:
# This action only runs when arg_dry_run is false
val:
- name: dry
data: arg_dry_run
when: 'contains:false' # skip if dry_run is true
- name: path
data: tag_project_path
- name: msg
data: tag_commit_message
action: cd {path} && git commit -m {msg}
When arg_dry_run is true, the dry candidate’s when fails → no
candidate wins for dry → dead tag → action skipped.
Placeholders & Escapes
The placeholder system connects val candidates to action templates and operator arguments. Two escape mechanisms handle literal braces and YAML whitespace.
Action templates
In the action field, {name} is replaced with the resolved value of
the matching val candidate:
val:
- name: content
data: query_raw
action: |
[Task]
Process {content}
Every {name} must reference a declared val candidate name. Undeclared
names are a validation error.
Shell quoting
For run: cmd actions, placeholder values are automatically
shell-quoted via shell_words::quote. You don’t need to add quotes
manually:
# Correct — engine handles quoting
action: cd {path} && git commit -m {msg}
# Wrong — double-quoting breaks the command
action: cd "{path}" && git commit -m "{msg}"
For run: tiny/small/medium/large/vision, values are substituted
as-is (no quoting).
Operator argument interpolation
Operator arguments in mods can reference tags via {name}:
mods: 'file:{tag_path}'
The engine replaces {name} with the current value of the referenced
tag. The tag must already be resolved — if not, it’s an error (declare
the dependency via data: in a val candidate).
Double-brace escape
When an LLM prompt needs literal braces (e.g. JSON templates, LaTeX), use double braces to escape them:
| Written | Resolves to | Use case |
|---|---|---|
{name} | value of name | Placeholder substitution |
{{X}} | {X} | Literal braces in LLM output |
{{X}} | {X} | Literal braces in operator args |
In action templates
{{X}} → {X} in the prompt sent to the LLM. This lets you include
JSON templates or other brace-based syntax without the engine treating
them as placeholders:
action: |
Output JSON: {{"name": "value", "count": 42}}
The LLM receives: "name": "value", "count": 42
In operator arguments
Same rule: {{X}} → {X}. This is how you pass literal braces through
the operator pipe parser, which uses | and : as separators:
# Pass a JSON-like string through replace
mods: 'replace:{{key}}:value'
# Resolves to: replace {key} → value
Brace-escape in operator parsing
The operator pipe parser splits on | and :, but ignores separators
inside braces. This means {X} and {{X}} protect their contents
from being split:
# The : inside braces is NOT treated as a name/arg separator
mods: 'replace:{from:with}:to'
# name = "replace", arg = "{from:with}:to"
# After unescape: arg = "from:with:to"
Unescape rules for operator args
After splitting, each arg is unescaped in a single left-to-right pass:
| Input | Output | Rule |
|---|---|---|
{X} | X | Unwrap — removes braces, escapes :/| |
{{X}} | {X} | Reduce — keeps braces, escapes :/| |
Use single braces when you want the value without braces (most common). Use double braces when the operator needs a literal brace-wrapped value.
Escape mnemonics
In operator arguments and each config, these sequences are expanded
after brace unescaping:
| Code | Expands to | Purpose |
|---|---|---|
\n | Newline | Line separator |
\t | Tab | Tab character |
\s | Space | Shields from YAML trimmers |
YAML trims trailing whitespace by default. \s prevents a trailing
space from being eaten:
# Join with comma-space
mods: 'join:,\s'
# Result: "item1, item2, item3"
# Split on tab
mods: 'split:\t'
# each with escape mnemonics
each:
split: '\n'
merge: '\x1F'
ITEM_SEP (\x1F)
Internally, list items are separated by ITEM_SEP (\x1F, ASCII Unit
Separator). This character never appears in normal text, so it’s safe as
an internal delimiter.
- Cross-step list data is joined with newlines for display.
- The
splitoperator producesITEM_SEP-separated lists. - The
joinoperator collapsesITEM_SEP-separated lists back to strings. ITEM_SEPis sanitized to\nin final output.
In each config, merge: '\x1F' preserves list structure across
fan-out steps — each item’s result stays separated for downstream list
operators in the next action.
Quick reference
| Context | {name} | {{X}} | \n/\t/\s |
|---|---|---|---|
| Action template | Placeholder substitution | Literal {X} in LLM | Not expanded |
| Operator arg | Tag interpolation + unwrap | Reduce to {X} | Expanded |
each.split/merge | Not interpolated | Not reduced | Expanded |
when/fail | Tag interpolation + unwrap | Reduce to {X} | Expanded |
Operators — Read
Read operators fetch data from the outside world into the pipeline.
They are world → value: no input validation beyond existence;
failure produces an Err (file not found, parse error, etc.).
Read operators are used in the mods field of a val candidate:
val:
- name: content
data: query_raw
mods: 'fetch|text'
Operators
ast — parse source code to JSON
Parses a source file into a structured JSON AST via vibe-ast.
| Syntax | Description |
|---|---|
ast | Brief output (auto-detect language) |
ast:brief | Signatures only — no function/class bodies |
ast:full | Full output with bodies and imports |
ast:<lang> | Explicit language code (overrides auto-detection) |
Supported language codes: rs, py, ts, js, java, go, cs,
kt, swift, dart, sh, bat, ets, md.
In brief mode, function/class bodies, imports, tags, markers, and
warnings are stripped. The path field is injected into the root JSON
object.
List-aware: processes each file path independently, returns one JSON object per file. Empty result for a file (e.g. parse failure) is dropped from the output.
fetch — download URL or resolve local file
| Syntax | Description |
|---|---|
fetch | URL → temp file path; existing local file → resolved |
- URL (
http:///https://) → downloads to a temp file (deduped by URL hash, extension from MIME). Returns the temp file path. - Existing local file → resolves to absolute path.
- Neither → passes the value through unchanged.
Per-item: works over list input.
resolve — resolve path to absolute
| Syntax | Description |
|---|---|
resolve | Resolve any path (~, ./, ../) |
resolve:dir | Resolve only if the path is a directory |
resolve:file | Resolve only if the path is a file |
Returns an empty string if the path doesn’t exist or doesn’t match the filter. Per-item.
scan — scan directory for file paths
| Syntax | Description |
|---|---|
scan | Scan directory tree, return list of file paths |
Input must be a directory path (resolves ~, ./, ../). Returns
items separated by ITEM_SEP (\x1F). Errors if the path is not a
directory. List-aware across multiple directory paths.
screenshot — interactive screen capture
| Syntax | Description |
|---|---|
screenshot | Opens interactive area selection, returns path |
Triggers the OS-native screenshot tool for area selection. Returns the saved image path (PNG). Ignores input value.
Platform support:
| OS | Tools (tried in order) |
|---|---|
| macOS | screencapture -i -x |
| Linux (Wayland) | Desktop-native first (gnome-screenshot, spectacle), then grim+slurp |
| Linux (X11) | scrot -s, maim -s, gnome-screenshot -a, import |
If the user cancels the selection (Esc), the operator returns an error. Temp screenshots older than 24 hours are cleaned up automatically.
text — extract text from files or content
| Syntax | Description |
|---|---|
text | Extract text from HTML/PDF/image file, or base64 passthrough |
Processing order per item:
- Base64 image → passed through as-is.
- HTML string → converted to plain text (
html2text). - File path (resolved) → tried in order:
- Image bytes → base64 encoded.
- PDF bytes → text extracted (
pdf_extract). - HTML content → converted to plain text.
- Otherwise → raw file content as UTF-8.
Errors if the file doesn’t exist or the content type is unsupported. Per-item.
Chaining with other operators
Read operators typically appear at the start of a mods chain, fetching
data that downstream transform/inspect/write operators then process:
# Fetch a URL, extract its text content
mods: 'fetch|text'
# Resolve a directory, scan it, parse AST, format as JSON
mods: 'resolve:dir|scan|ast|format:json'
# Read a file path, extract text, strip markdown fences
mods: 'fetch|text|strip'
See Transform and Inspect for downstream operators.
Operators — Inspect
Inspect operators are value → bool predicates. They return the string
"true" or "false" per item and are used in when and fail guards
on val candidates.
All inspect operators support the :not suffix to invert the result.
val:
- name: content
data: query_raw
when: 'is:empty:not' # proceed only if non-empty
fail: 'contains:ERROR' # bail if the value contains ERROR
Operators
contains — substring test
| Syntax | Description |
|---|---|
contains:<X> | True if value contains X |
when: 'contains:TODO'
when: 'contains:error:not' # true if value does NOT contain "error"
equals — exact equality
| Syntax | Description |
|---|---|
equals:<X> | True if value equals X exactly |
when: 'equals:true'
when: 'equals:0:not' # true if value is NOT "0"
matches — regex test
| Syntax | Description |
|---|---|
matches:<re> | True if value matches the regex |
Invalid regex is a hard error.
when: 'matches:^[0-9]+$' # true if value is digits only
when: 'matches:^(feat|fix):not' # true if value does NOT start with feat/ or fix:
compare — numeric comparison
| Syntax | Description |
|---|---|
compare:<mode>:<N> | Compare value against numeric threshold |
Modes:
| Mode | Meaning |
|---|---|
gt | value > N |
lt | value < N |
gte | value ≥ N |
lte | value ≤ N |
- Non-numeric value →
"false"(not an error). - Non-numeric threshold → hard error.
- Unknown mode → hard error.
when: 'compare:gt:0' # true if value is a number greater than 0
when: 'compare:lte:100:not' # true if value is NOT ≤ 100 (or non-numeric)
is — type/presence predicate
| Syntax | Description |
|---|---|
is:<kind> | True if value matches the given kind |
Kinds:
| Kind | Checks |
|---|---|
empty | Value is empty string |
num | Value parses as a float |
int | Value parses as an integer |
bool | Value is "true" or "false" |
url | Value parses as a valid URL |
path | Value is an existing filesystem path |
json | Value parses as valid JSON |
when: 'is:empty:not' # proceed only if value is non-empty
when: 'is:path' # proceed only if value is an existing path
when: 'is:json:not' # true if value is NOT valid JSON
Inversion (:not)
All inspect operators accept :not as a suffix after the arg. It swaps
"true" ↔ "false". Applied after the operator, not before:
# Equivalent ways to express "not empty":
when: 'is:empty:not'
Per-item evaluation
All inspect operators are per-item over lists. When the input is a list
(separated by ITEM_SEP), each item is evaluated independently, and the
results are joined back into a list of "true"/"false" strings.
This matters when when/fail guards evaluate list data — every item
must pass for the candidate to proceed.
Operators — Transform
Transform operators are value → value pure functions. They never touch the outside world — no files, no network, no clipboard.
Most operators are list-aware: they adapt to the input kind.
If the input contains ITEM_SEP (\x1F), it’s treated as a list;
otherwise as a scalar. The operator documentation below notes the
behavior for each kind.
Scalar operators
These accept scalar input only. List input is rejected.
base64 — encode / decode
| Syntax | Description |
|---|---|
base64:encode | Encode value to base64 |
base64:decode | Decode base64 to string |
Decode failure → empty string (not an error).
mods: 'base64:encode'
mods: 'base64:decode'
split — split string to list
| Syntax | Description |
|---|---|
split | Split by newline (default) |
split:<sep> | Split by separator |
Accepts scalar only; always produces a list. Supports escape mnemonics
in the separator (\n, \t, \s).
mods: 'split' # split by newline
mods: 'split:,\s' # split by comma-space
List → scalar operators
join — collapse list to string
| Syntax | Description |
|---|---|
join | Join with newline (default) |
join:<sep> | Join with custom separator |
Supports escape mnemonics.
mods: 'join' # newline-separated
mods: 'join:,\s' # comma-space separated
item — Nth list element
| Syntax | Description |
|---|---|
item:<N> | Return element at index N (0-based) |
Negative N counts from the end (item:-1 = last). Out of range →
empty string. On scalar input: returns the whole string if N is 0,
else empty.
mods: 'item:0' # first element
mods: 'item:-1' # last element
size — count / length
| Syntax | Description |
|---|---|
size | List → item count; scalar → byte length |
Always returns a numeric string.
mods: 'size'
List filter operators
filter — keep / remove items by pattern
| Syntax | Description |
|---|---|
filter:<X> | Remove items containing X (substring) |
filter:eq:<X> | Remove items exactly equal to X |
filter:not:<X> | Keep items containing X (inverted) |
mods: 'split|filter:eq:-' # remove items equal to "-"
mods: 'split|filter:not:error' # keep items containing "error"
grep — regex filter
| Syntax | Description |
|---|---|
grep:<re> | Keep items matching regex |
grep:<re>:not | Remove items matching regex |
On scalar input: returns the whole string on match, else empty string.
mods: 'split|grep:^(feat|fix)' # keep conventional commit lines
mods: 'split|grep:^\\s*$:not' # remove blank lines
List sort / deduplicate operators
sort — alphabetical sort
| Syntax | Description |
|---|---|
sort | Sort ascending (default) |
sort:asc | Sort ascending |
sort:desc | Sort descending |
On scalar: sorts characters.
mods: 'split|sort'
mods: 'split|sort:desc'
uniq — deduplicate
| Syntax | Description |
|---|---|
uniq | Remove duplicate items / characters |
Preserves first occurrence order.
mods: 'split|uniq'
reverse — reverse order
| Syntax | Description |
|---|---|
reverse | List → reverse item order; scalar → reverse chars |
mods: 'split|reverse'
List slice operators
take — first N elements
| Syntax | Description |
|---|---|
take:<N> | List → first N items; scalar → first N chars |
mods: 'split|take:5'
tail — last N elements
| Syntax | Description |
|---|---|
tail:<N> | List → last N items; scalar → last N chars |
mods: 'split|tail:3'
Per-item operators
These apply independently to each list item (or the whole scalar).
lower / upper — case conversion
| Syntax | Description |
|---|---|
lower | To lowercase |
upper | To UPPERCASE |
mods: 'split|lower'
mods: 'upper'
replace — substring replacement
| Syntax | Description |
|---|---|
replace:<from>:<to> | Replace all occurrences |
mods: 'replace:old:new'
trim — trim whitespace / chars
| Syntax | Description |
|---|---|
trim | Strip whitespace from ends; remove empty list items |
trim:<chars> | Strip whitespace + chars; remove items equal to chars |
mods: 'trim' # strip whitespace, drop blanks
mods: 'trim:#' # strip whitespace and '#', drop items equal to '#'
strip — remove Markdown fences
| Syntax | Description |
|---|---|
strip | Remove ```lang\\n...\\n```, ~~~, `, """ wrappers |
Auto-detects the wrapper type. Returns the inner content, trimmed.
mods: 'strip'
default — fallback for empty
| Syntax | Description |
|---|---|
default:<X> | If value is empty → X, else passthrough |
Per-item: each empty list item is replaced with X.
mods: 'default:N/A'
Format conversion
format — convert between data formats
| Syntax | Description |
|---|---|
format:json | Convert to JSON |
format:json5 | Convert to JSON5 |
format:yaml | Convert to YAML |
format:toml | Convert to TOML |
Auto-detects input format (tries JSON, JSON5, YAML, TOML in order). List-aware: each item is parsed independently, then wrapped into an array for JSON/JSON5 output, concatenated for YAML. TOML does not support top-level arrays.
mods: 'format:json'
mods: 'scan|ast|format:json'
Escape mnemonics
Operator arguments support \n, \t, \s escapes (e.g. join:,\s).
Full escape system — see Placeholders & Escapes.
Operators — Write
Write operators are value → world (pass-through). They emit data to the outside world and return the input value unchanged, so downstream operators in the chain continue to see the original value.
Operators
clipboard_text — copy text to clipboard
| Syntax | Description |
|---|---|
clipboard_text | Copy value to system clipboard (pass-through) |
Outer Markdown code fences are stripped before copying. This ensures clean text reaches the clipboard even when the pipeline result is wrapped in a code block.
JSON mode: no-op — see Output Modes.
val:
- name: result
data: tag_output
mods: 'clipboard_text'
clipboard_image — copy image to clipboard
| Syntax | Description |
|---|---|
clipboard_image | Copy base64 PNG image to system clipboard (pass-through) |
Input must be a base64-encoded image string.
JSON mode: no-op — see Output Modes.
val:
- name: image
data: tag_screenshot
mods: 'clipboard_image'
file — write value to file
| Syntax | Description |
|---|---|
file:<path> | Write value to file (overwrite) |
file:<path>:append | Append value to file |
The path is unescaped for brace-escape sequences. Empty path is a hard error. Write failure is a hard error.
Always passes the input value through, regardless of write outcome.
# Overwrite
val:
- name: saved
data: tag_json
mods: 'file:/tmp/output.json'
# Append
val:
- name: log
data: tag_entry
mods: 'file:/tmp/log.txt:append'
Pass-through behavior
Write operators always return their input unchanged:
input → [write op] → input (side effect: write to clipboard/file)
This means write operators can appear anywhere in a mods chain, and
subsequent operators see the original value:
# Save to file AND copy to clipboard
mods: 'file:/tmp/result.txt|clipboard_text'
See Output Modes for details.
Providers — Query
Query providers resolve query_* tags from user input — CLI positional
args, clipboard, or IDE plugin data. They are resolved once at pipeline
startup (before the engine loop) and stored in input_tags.
Contract
- Each
query_*provider receives the raw positional argument (if any). - Non-empty result wins. Empty input →
""— the val candidate is skipped and the next candidate for the same name is tried. - Exception:
query_imagereturnsErrif the input is not a valid image (hard failure, not empty). - Empty query value without a
whenguard is a startup error (ambiguous input). Addwhen: 'is:empty:not'to explicitly handle missing input.
Usage in a val candidate:
val:
- name: content
data: query_raw
when: 'is:path'
mods: 'fetch|text'
- name: content
data: query_raw
fail: 'is:empty:not'
Tags
query_raw — raw input, no validation
| Tag | Behavior |
|---|---|
query_raw | Positional arg as-is; empty if not provided |
No type checking, no path resolution. Use when the input might be text, a path, a URL — anything.
query_prompt — interactive prompt marker
| Tag | Behavior |
|---|---|
query_prompt | Returns raw value; signals interactive fallback |
This is a presence marker, not a data provider. When query_prompt
appears in any val chain and the query slot is empty, the CLI triggers
an interactive terminal prompt. In IDE mode, the plugin opens an input
dialog.
The prompt itself fires at a fixed early point in the flow — not during val resolution.
query_clipboard — combined clipboard
| Tag | Behavior |
|---|---|
query_clipboard | Combined clipboard content by priority: text → paths → image |
Reads clipboard content trying each type in order. Token-limited to the
largest num_ctx across all cluster nodes (prevents oversized clipboard
input from blowing context windows).
query_clipboard_text — clipboard text
| Tag | Behavior |
|---|---|
query_clipboard_text | Raw text from the clipboard |
query_clipboard_path — clipboard file paths
| Tag | Behavior |
|---|---|
query_clipboard_path | Copied file paths from the clipboard |
Multiple paths are newline-separated.
query_clipboard_image — clipboard image
| Tag | Behavior |
|---|---|
query_clipboard_image | Clipboard image as base64 PNG string |
query_file_path — file path from input
| Tag | Behavior |
|---|---|
query_file_path | Explicit input → existing file path; "" if not found |
Resolves the positional arg to an absolute path. Supports file:// URI
format from IDE plugins. Returns "" if no file exists at the path —
this makes the when: 'is:empty:not' pattern work for conditional
file-based candidates.
query_project_path — project root path
| Tag | Behavior |
|---|---|
query_project_path | Input → project root directory; "" if not found |
Walks up from the input path looking for a project marker, then returns the root directory. Recognized markers:
.git, .hg, Cargo.toml, package.json, go.mod, pom.xml,
build.gradle, .idea
Stops at the home directory or filesystem root. Returns "" if no
marker is found.
query_line — first line of input
| Tag | Behavior |
|---|---|
query_line | First line of the input text; "" if empty |
Useful for single-line IDE inputs (cursor line, selection first line).
query_image — image input
| Tag | Behavior |
|---|---|
query_image | Input (URL/file/base64) → validated base64; Err if invalid |
Accepts three input forms:
- URL (
http:///https://) → downloads, encodes to base64. - File path → reads file, encodes to base64.
- Base64 string → decoded and validated.
Always validates that the bytes form a real image. Invalid input is a
hard error — use a when guard or a fallback candidate to handle
missing images gracefully.
Resolution flow
Query providers are resolved early, before the engine loop:
apply_args()— CLI flags →input_tags.apply_query_tags()— eachquery_*in val candidates →input_tags.validate_query_tags()— reject empty query values without awhenguard.
After this, input_tags is frozen and the engine begins resolving
actions.
CLI vs IDE
| Tag | CLI source | IDE source |
|---|---|---|
query_raw | Positional arg or clipboard | Editor selection text |
query_prompt | Interactive terminal prompt | IDE input dialog |
query_file_path | Arg → existing file path | Path to current file |
query_project_path | Arg → project root | Project root path |
query_line | First line of arg | Cursor line |
query_image | Arg → URL/file/base64 | Screenshot or selected image |
The IDE plugin fills the query slot based on the api.input field in
the YAML manifest — see IDE Plugin.
Providers — System
System providers resolve system_* tags at runtime. They read
environment/OS state — no input, no user interaction.
Contract
- Always return
Ok(String). - Missing value → empty string (
""), neverErr. - Resolved once per pipeline run, then cached for all actions.
Usage in a val candidate:
val:
- name: lang
data: system_language
Tags
Identity
| Tag | Value | Example |
|---|---|---|
system_os | Operating system name | macos, linux |
system_arch | CPU architecture | aarch64, x86_64 |
system_hostname | Machine hostname | zarubin-mini |
system_user | Current user name ($USER) | keygenqt |
system_uid | Current user ID | 501 |
system_pid | Current process ID | 42831 |
system_shell | Shell basename ($SHELL) | zsh, bash |
system_language | Language code from $LANG | en, ru |
system_language strips locale suffixes: en_US.UTF-8 → en.
Falls back to en if $LANG is unset, C, or POSIX.
Code languages
| Tag | Value | Example |
|---|---|---|
system_code_langs | Supported code language extensions | rs, py, ts, js, … |
system_code_shell | Supported shell language extensions | sh, bat |
Both tags are newline-separated lists, one extension per line.
Single source of truth: vibe_ast languages (crate::langs).
Time
| Tag | Value | Example |
|---|---|---|
system_date | Current date, ISO 8601 | 2025-01-15 |
system_time | Current time | 14:30:05 |
system_datetime | Current date and time, ISO 8601 | 2025-01-15T14:30:05 |
system_timestamp | Unix epoch seconds | 1736945405 |
Hardware
| Tag | Value | Example |
|---|---|---|
system_cpu_cores | Logical CPU core count | 8 |
system_mem_available | Available memory in bytes | 4294967296 |
Directories
| Tag | Value | Example |
|---|---|---|
system_dir_home | User home ($HOME) | /Users/keygenqt |
system_dir_pwd | Current working directory | /Users/keygenqt/project |
system_dir_config | User config directory | ~/Library/Application Support (macOS) |
system_dir_data | User data directory | Platform-specific |
system_dir_cache | User cache directory | Platform-specific |
system_dir_download | User downloads directory | Platform-specific |
system_dir_temp | Temporary directory | /tmp, /var/folders/... |
All directory tags use the dirs crate for cross-platform resolution.
Missing directory → empty string.
Example
actions:
- tag: tag_report
run: small
val:
- name: lang
data: system_language
- name: os
data: system_os
- name: user
data: system_user
- name: date
data: system_date
action: |
[Task]
Generate a short system report.
Language: {lang}.
[Data]
OS: {os}
User: {user}
Date: {date}
Built-in Actions
Vibe Action embeds two actions in the binary: docs and info.
Additional commands ship in the
vibe-action-groups
repository. The default config already connects it via action groups, so
these commands are available out of the box as vibe-action <group> <action>.
Groups are declared in config.yaml — you can add your own repositories
or local directories the same way. See Configuration
for the groups block and the repository for its groups and sources.
Quick Reference
| Action | About | Input | Output |
|---|---|---|---|
docs | Ask a question about Vibe Action | query_prompt | dialog |
info | Show system information | (none) | dialog |
Query type semantics (query_prompt, …) are described in
Query Providers. Output targets (replace,
clipboard, dialog) — in IDE Plugin.
Auto-update
On first run, built-in actions are written to ~/.vibe-action/actions/.
On every start, the engine compares the on-disk version field with
PIPELINE_VERSION (currently 0.0.2). If they differ, the file is
overwritten with the embedded default. This keeps built-in actions in
sync with the engine — but it also means manual edits to a built-in
file are lost when its version bumps.
Grouped actions are never touched: only the embedded files
(docs, info) are subject to the version check.
Override the actions directory with VIBE_ACTION_PATH — see
CLI Reference.
Customizing
To modify a built-in action, copy it to a new file with a different
name. The original will be reset on version bumps; your copy won’t.
See Custom Actions for writing pipelines from scratch, and Configuration for action groups.
Custom Actions
Add your own YAML files to ~/.vibe-action/actions/ — they are loaded
automatically alongside the built-in defaults. The file name (without
.yaml) becomes the CLI subcommand unless name overrides it.
Minimal action
version: '0.0.2'
name: hello
about: Say hello
actions:
- tag: tag_greeting
run: value
val:
- name: input
data: query_raw
action: 'Hello, {input}!'
vibe-action hello World
# → Hello, World!
File-path / text dual mode
A common pattern: if the input is a valid path, read the file; otherwise treat it as inline text.
actions:
- tag: tag_content
run: value
reg: '.+'
val:
- name: content
data: query_raw
when: 'is:path'
mods: 'fetch|text'
- name: content
data: query_raw
fail: 'is:empty:not'
action: '{content}'
CLI arguments
Define flags; each is available in val candidates as data: <name>
(convention: prefix names with arg_):
args:
- name: arg_style
short: 's'
input: string
default: concise
help: 'Output style'
vibe-action my-action --arg_style verbose
Argument values are accessed as data: arg_style in val candidates.
LLM prompt with system tags
Use system_* providers to adapt prompts to the user’s environment:
actions:
- tag: tag_result
run: medium
val:
- name: lang
data: system_language
- name: content
data: query_raw
action: |
[Task]
Respond in {lang}.
{content}
Multi-step pipeline
Actions can chain results via data referencing other action tags. The
engine resolves dependencies automatically:
actions:
- tag: tag_path
run: value
val:
- name: dir
data: system_dir_download
- name: pid
data: system_pid
action: '{dir}/output-{pid}.txt'
- tag: tag_save
run: value
val:
- name: content
data: query_raw
mods: 'file:{tag_path}'
action: 'Saved to {tag_path}'
tag_save depends on tag_path — the engine runs tag_path first.
Shell commands
run: cmd executes via sh -c. Values are shell-quoted automatically:
actions:
- tag: tag_files
run: cmd
val:
- name: path
data: query_project_path
action: find {path} -type f -name '*.rs'
Fan-out with each
Process each item in a list independently, then merge results:
actions:
- tag: tag_results
run: small
val:
- name: item
data: tag_items
each:
split: '\n'
merge: '\n'
action: |
[Task]
Summarize: {item}
IDE integration
Add an api block so the IDE plugin knows how to handle the action:
api:
output: replace
input: query_raw
args:
arg_file: query_file_path
See IDE Plugin for the full API reference.
Customizing built-in actions
Do not edit built-in files directly — they are reset on version bumps. See Built-in Actions.
Validation
All custom actions are validated on startup. Common errors:
| Error | Fix |
|---|---|
| Version mismatch | Set version to current PIPELINE_VERSION |
Empty name or about | Add required fields |
Duplicate tag across actions | Use unique tag names |
data references own tag | Remove self-reference |
Bare query in data | Use query_raw instead |
query_*/system_* prefix on tag | Rename the tag |
Unknown operator in mods | Check operator name and spelling |
Non-inspect operator in when/fail | Use inspect operators only |
Undeclared {name} in action | Add a val candidate with that name |
Tips
- Start simple — a single
run: valueorrun: smallaction is enough for most use cases. - Use
reg— validate output format early. A loose LLM can produce unexpected structure;reg: '.+'catches empty results. - Use
failguards — catch empty inputs before they reach the LLM:fail: 'is:empty:not'. - Use
whenguards — avoid unnecessary work (e.g. don’t take a screenshot if the clipboard already has an image). - Use
ask: true— for destructive shell commands (git push, file deletion), require user confirmation. - Test with
--help— your action and its args appear in the help output automatically.
Configuration
Vibe Action uses a single YAML config file at ~/.vibe-action/config.yaml.
Created automatically on first run. Override the path with VIBE_CONFIG.
Config file structure
version: '0.0.3'
action:
system: |-
You are Vibe Action — a CLI tool, not a chatbot.
Work fast. Just do the task.
Output ONLY the requested result.
retries: 2
groups:
- git: https://github.com/github/vibe-action-groups.git
path: /code
name: code
about: Work with code
cluster:
- provider: ollama
host: http://localhost:11434
model: qwen2.5-coder:3b-instruct
role: small
timeout_secs: 30
temperature: 0.1
seed: 42
num_ctx: 4096
num_predict: 2048
parallel: 1
Version
Top-level version tracks the config schema. The engine compares it with
its internal CONFIG_VERSION on startup — a mismatch is a hard error.
Fix: bump version in your config, or delete ~/.vibe-action/config.yaml
and restart (fresh defaults are written).
Action settings
| Field | Type | Default | Description |
|---|---|---|---|
system | string | (see code) | Global system prompt for all LLM calls |
retries | integer | 2 | Retries for failed LLM steps (0 = off) |
The default system prompt instructs the model to be brief and output only the requested result — no explanations, no Markdown fences unless asked.
Action groups
groups declares nested command namespaces: vibe-action <group> <action>.
Each group sources its YAML actions from a git repository or a local
directory.
| Field | Type | Required | Description |
|---|---|---|---|
git | string | git source | Repository URL, cloned once into the cache |
path | string | local source; optional with git | Directory with YAML actions, or subfolder inside the clone (/ = repo root) |
ref | string | no | Branch, tag, or commit to pin (git source only) |
name | string | yes | CLI group name |
about | string | yes | Short description shown in help |
Either git or path must be set. With a git source, path selects a
subfolder of the clone and ref pins a specific branch, tag, or commit.
A git source is cloned once into the cache and re-used on later runs.
vibe-action clean removes the cloned groups, so they are re-pulled on
the next run.
Local directory group
groups:
- path: /Users/me/vibe-actions/code
name: code
about: Work with code
Group validation
namemust be non-empty: lowercase letters, digits,_, or-.namemust not collide with a system command or a top-level action.aboutmust be non-empty.refis only allowed with agitsource.- With
git,pathmust stay inside the clone (no..).
Cluster
Define one or more LLM provider nodes. The engine routes each pipeline
step to nodes whose role matches the step’s run size
(see YAML Format).
Cluster node fields
| Field | Type | Required | Description |
|---|---|---|---|
provider | string | yes | ollama, deepseek, qwen, kimi, zhipu |
host | string | yes | API endpoint URL |
model | string | yes | Model name |
role | string | no | tiny, small, medium, large, vision |
timeout_secs | integer | yes | Request timeout in seconds |
temperature | float | yes | 0.0–2.0, lower = more deterministic |
seed | integer | yes | Random seed for reproducibility |
num_ctx | integer | yes | Context window size in tokens |
num_predict | integer | yes | Max tokens to generate |
api_key | string | no | API key for cloud providers |
parallel | integer | yes | Concurrent connections (default: 1) |
Role-based routing
Each pipeline action declares a run size. The engine filters cluster
nodes by matching role:
run value | Matches role |
|---|---|
tiny | tiny |
small | small |
medium | medium |
large | large |
vision | vision |
- No node with the requested role → fallback: all nodes are used.
- Node without a
role→ responds to every request. - Missing role at pipeline start → warning printed.
Multi-node example
cluster:
- provider: ollama
host: http://localhost:11434
model: qwen2.5-coder:3b-instruct
role: small
timeout_secs: 30
temperature: 0.0
seed: 42
num_ctx: 4096
num_predict: 512
parallel: 2
- provider: deepseek
host: https://api.deepseek.com/v1
model: deepseek-v4-flash
role: large
timeout_secs: 120
temperature: 0.1
seed: 42
num_ctx: 16384
num_predict: 8192
api_key: sk-...
parallel: 2
- provider: ollama
host: http://localhost:11434
model: qwen2.5vl:7b
role: vision
timeout_secs: 120
temperature: 0.4
seed: 42
num_ctx: 8192
num_predict: 8192
parallel: 1
File layout
~/.vibe-action/
├── config.yaml
└── actions/
├── docs.yaml
└── info.yaml
- Config:
~/.vibe-action/config.yaml— override withVIBE_CONFIG. - Actions:
~/.vibe-action/actions/— override withVIBE_ACTION_PATH. - Both directories are created on first run. Custom
.yamlfiles in the actions directory are loaded automatically. - Cloned group repositories live in the application cache directory, not under
~/.vibe-action/.
Initialization order
- Output registry from
VIBE_LOG_TYPE/VIBE_TRACE_LEVEL. - Config file from
VIBE_CONFIGor default path; create if missing. - Validate config (version, cluster nodes).
- Load pipelines (scan actions dir and action groups, validate, cache).
Output Modes
Vibe Action routes all output through a single strategy selected at startup.
No println! exists outside the output module — everything flows through
print_template! / print_text! macros.
Selecting a mode
Set VIBE_LOG_TYPE before startup:
VIBE_LOG_TYPE=json vibe-action code review main.rs
| Value | Strategy | Use for |
|---|---|---|
| (unset) | cli | Interactive terminal (default) |
cli | cli | Interactive terminal |
plain | plain | CI, piped output, scripts |
json | json | IDE plugin / Kotlin-Compose UI |
tracing | tracing | Debugging, detailed structured logs |
| (auto) | test | Internal test mode (VIBE_TEST=1) |
Mode details
cli — terminal
ANSI-colored messages with semantic labels:
- error — red bold
error: - warning — yellow bold
warning: - info — blue bold
info: - progress — cyan bold, carriage-return overwrite for percentages
- success — green framed block with Markdown rendering and syntax highlighting
Template placeholders support inline styling:
{key|color|style} — e.g. {tag|bright_green|bold}.
Success blocks render Markdown via termimad and highlight code via
syntect (base16-eighties dark theme). Language aliases are resolved
automatically (arkts→typescript, csharp→cs, batch→bat, etc.).
plain — unformatted
No ANSI codes, no framing, no Markdown rendering. Errors and warnings go to stderr; everything else to stdout. Use in CI pipelines or when piping output to other tools.
json — structured
Each message is a JSON object on stdout:
{
"level": "success",
"value": {
"message": "result text"
}
}
Fields are the key-value pairs from the template. Outer Markdown code fences are stripped from string values.
Clipboard write operators (clipboard_text, clipboard_image) are
no-ops in JSON mode — the plugin owns the buffer. Use api.output: clipboard in the YAML manifest to have the plugin copy the result —
see IDE Plugin.
Messages are tagged with an export context for plugin routing:
| Context | Purpose |
|---|---|
actions | Action list / status |
status | System status response |
success | Final action result |
confirm | User confirmation prompt |
tracing — structured logs
Delegates to the tracing crate. Set VIBE_TRACE_LEVEL to control
verbosity (only effective when VIBE_LOG_TYPE=tracing):
| Level | Shows |
|---|---|
error | Errors only |
warn | Warnings + errors |
info | Flow progress + results |
debug | Engine internals |
trace | Maximum verbosity |
test — minimal
Only error and success pass through; all other kinds are no-ops.
Activated automatically when VIBE_TEST=1. Used for integration test
assertions.
Message model
Every message is an OutputMsg with:
- kind — semantic level (
plain,info,success,warning,error,debug,trace,progress) - template — layout string with
{key}placeholders - fields — key-value map (JSON values)
The formatter resolves placeholders per mode:
CLI applies {key|color|style} styling; plain/json substitute values
without styling.
CLI Reference
Usage
vibe-action <action> [query] [args...]
vibe-action docs "How to use the 'contains' operator?"
vibe-action info
vibe-action --help
Each <action> maps to a YAML-defined action. Use --help to list all
available actions and their arguments:
vibe-action --help
vibe-action docs --help
Action groups
Actions from external repositories or local directories are nested under a group command:
vibe-action <group> <action> [query] [args...]
vibe-action code review main.rs
vibe-action project commit .
Groups are declared in config.yaml and loaded on startup — see
Configuration.
vibe-action <group>prints the group’s action list.vibe-action <group> <action> --helpprints action-specific help.
System commands
vibe-action status # Show version, paths, action counts
vibe-action clean # Remove stale cache, temp files, clipboard
vibe-action stop # Stop all running vibe-action processes
Action arguments
Actions define their own CLI arguments in YAML. See
YAML Format for the args schema.
vibe-action project commit -d # dry-run flag (bool)
vibe-action data extract -f log.txt # file argument (string)
Singleton guard
By default, only one vibe-action process runs at a time. Starting a
new action automatically stops the previous one (graceful shutdown with
a 3-second timeout, then force-kill). This keeps shared state
(cache, local LLMs) predictable.
Disable with VIBE_SKIP_LOCK:
VIBE_SKIP_LOCK=1 vibe-action code review main.rs
Useful for parallel API clusters or when running multiple actions concurrently.
Interactive confirm
Actions with ask: true prompt for user approval before execution:
- CLI —
inquireconfirm dialog with the command preview. - JSON mode — confirm protocol via stdin/stdout (30-second timeout, fail-closed).
Interactive prompt
Actions using query_prompt trigger an interactive input when no query
is provided on the command line:
- CLI —
inquiretext prompt labeled “Query”. - IDE — plugin opens an input dialog.
Desktop notifications
Actions with notify: true send a desktop notification on completion
(CLI mode only):
- macOS — via
terminal-notifier(install:brew install terminal-notifier). - Linux — via
notify-rust(freedesktop.org D-Bus spec).
Role mismatch warning
If a pipeline uses a run size (e.g. large) but no cluster node has
the matching role, the engine warns and offers to continue with all
available nodes (fallback). In CLI mode this is a confirm dialog; in
JSON mode it uses the confirm protocol.
See Configuration for cluster role setup.
Environment variables
| Variable | Description | Default |
|---|---|---|
VIBE_CONFIG | Path to config file | ~/.vibe-action/config.yaml |
VIBE_ACTION_PATH | Path to actions directory | ~/.vibe-action/actions/ |
VIBE_SKIP_LOCK | Disable singleton guard for parallel execution | Not set (guard enabled) |
VIBE_TEST | Internal test mode (1 = enabled) | Not set |
Output-related variables (VIBE_LOG_TYPE, VIBE_TRACE_LEVEL) and trace
levels — see Output Modes.
Exit codes
| Code | Description |
|---|---|
0 | Success |
1 | Error (validation, shell command, LLM failure) |
130 | Instance superseded by a newer run (auto-cancelled) |
IDE Integration (VS Code & IntelliJ)
Vibe Action works perfectly from the terminal, but you can supercharge your workflow with the Vibe Action Cross IDE plugin.
Instead of manually configuring tasks.json in VS Code or External Tools in IntelliJ, the plugin provides a native UI panel inside your editor. It communicates directly with the vibe-action CLI to discover all available actions, letting you run them with visible, real-time feedback.
Why Use the Plugin?
- Zero Configuration: no need to edit JSON/XML files. The plugin automatically fetches all available actions from the
vibe-actionCLI — including built-in commands, your custom YAML flows, and any modifications you’ve made to the defaults. - Context Awareness: automatically passes selected text, file paths, or project context to your actions using query tags.
- Native UI: real-time progress feedback inside the IDE instead of waiting for terminal windows or system notifications.
- Seamless Output: results can automatically replace selected code, be copied to the clipboard, or appear in a native dialog window.
Prerequisites
Vibe Action CLI must be installed and available on your system PATH.
cargo install vibe-action
Get the plugin: download the latest pre-built artifacts (.vsix and .zip) from the GitHub releases, or build them yourself from the vibe-action-cross repository source.
VS Code Setup
- Open VS Code.
- Go to the Extensions view (
Cmd+Shift+XorCtrl+Shift+X). - Click the
...menu in the top-right corner of the Extensions panel. - Select Install from VSIX….
- Navigate to the downloaded file and select
vibe-action-<version>.vsix. - Reload VS Code when prompted.
Alternatively, install via CLI:
code --install-extension vibe-action-<version>.vsix
Once installed, open the Vibe Action panel from the activity bar. You will see a list of all your available actions. Click any action to run it, or use the provided keyboard shortcuts.
IntelliJ IDEA Setup
- Open IntelliJ IDEA.
- Go to
Settings/Preferences->Plugins. - Click the gear icon (
⚙️) in the top-right corner of the Plugins window. - Select Install Plugin from Disk….
- Navigate to the downloaded file and select
vibe-action-<version>.zip. - Restart IntelliJ IDEA when prompted.
Once installed, open the Vibe Action tool window (usually located on the right sidebar). The UI is rendered natively using Compose Multiplatform and matches the IntelliJ theme.
The api Block
The optional api block in a YAML action tells the plugin where to take input from and how to present the result. The CLI runtime ignores it.
version: 0.0.2
name: upper
about: Convert text to UPPERCASE
api:
output: replace # replace | clipboard | dialog
input: query_raw # any query tag, e.g. query_raw, query_prompt, query_image
args: # Optional: extra inputs beyond the main query
arg_file: query_file_path
actions:
- tag: tag_upper
run: value
val:
- name: text
data: query_raw
mods: 'upper'
action: '{text}'
Output Targets (api.output)
replace— the plugin replaces the currently selected text in your editor with the action’s result.clipboard— the plugin copies the result to your system clipboard.dialog— the result is shown in a native IDE popup/dialog.
Note: prefer output: clipboard over the clipboard_text / clipboard_image operators in pipelines meant for the IDE — those operators are no-ops when the action is driven by the plugin.
Input Sources (api.input)
The input field names the query tag the plugin fills before executing the CLI: query_raw (raw text), query_prompt (interactive dialog), query_file_path, query_project_path, query_line, query_image, and others. The full list with CLI and IDE behavior lives in Query Providers.
If an action does not have an api block, the plugin simply executes it and falls back to standard CLI behavior.
Extra Inputs (api.args)
args maps argument names to query tags. Use it when a flow needs
multiple inputs beyond the main query: each key is an argument name
(matching an args entry), each value is a query tag. The IDE collects
them and passes them to the CLI automatically.