Skip to content

Actions

Actions specify what changes to make to your notes' YAML front matter.

Action Syntax

SCALAR operations (no FOR prefix):

SET field "value"             # Set field value
DELETE field                  # Delete field
RENAME old TO new            # Rename field
INCREMENT field 5            # Increment field
DECREMENT field 5            # Decrement field

COLLECTION operations (FOR prefix required):

FOR array APPEND "item"      # Append to array
FOR array WHERE <condition> SET field "value"  # Conditional update
FOR array SORT BY field      # Sort array
FOR object MERGE {data}      # Merge object

Key insight: Scalar operations work on single values (no FOR), collection operations work on arrays/objects (FOR required).

Examples:

SET status "active"
DELETE old_field
FOR tags APPEND "reviewed"
FOR tasks WHERE status = "pending" SET status "active"

Core Action Operations

SET - Create or Update Fields

What it does: Sets a field value, creating it if missing or updating it if it exists.

Unique power: Guarantees a field has a specific value and type, regardless of its current state. Can normalize heterogeneous data (e.g., convert string → array).

Syntax:

SET <field> <value>

Example that can't be done any other way:

Ensure tags is always an array with at least one value, even if it's currently a string or missing:

yaml
SET tags ["unprocessed"]

Before (various states):

yaml
# Case 1: Field missing
title: My Note

# Case 2: Field is string
title: My Note
tags: old-string-value

# Case 3: Field is already array
title: My Note
tags: [existing, values]

After (normalized):

yaml
# All cases result in same structure
title: My Note
tags: [unprocessed]

Other examples:

SET status "active"
SET priority 5
SET metadata {author: "John", version: 2}

FOR ... APPEND - Append to Lists (De-duplicated)

What it does: Adds an item to an array only if it's not already present.

Unique power: Build lists incrementally across multiple rules without creating duplicates. Perfect for workflows where multiple conditions might fire.

Syntax:

FOR <array> APPEND <value>

Example that can't be done any other way:

Mark a note as reviewed without creating duplicate tags, even if the rule runs multiple times:

yaml
FOR tags APPEND "reviewed"

Before:

yaml
---
title: Project Note
tags: [project, active]
---

After (first run):

yaml
---
title: Project Note
tags: [project, active, reviewed]
---

After (second run - no duplicate):

yaml
---
title: Project Note
tags: [project, active, reviewed]  # ← Still only one "reviewed"
---

Why you need APPEND: SET tags would overwrite existing tags. FOR tags INSERT AT -1 would create duplicates. Only APPEND provides idempotent list building.


FOR ... REMOVE - Delete from Lists (Conditional)

What it does: Removes items from an array that match a condition.

Unique power: Selectively delete items from lists based on their values while preserving other items.

Syntax:

FOR <array> WHERE <condition> REMOVE

Example that can't be done any other way:

Remove "draft" tag while keeping all other tags intact:

yaml
FOR tags WHERE $ = "draft" REMOVE

Before:

yaml
---
title: Published Article
tags: [draft, project, active, important]
---

After:

yaml
---
title: Published Article
tags: [project, active, important]  # ← Only "draft" removed
---

Note: The $ symbol represents the current item in the array.


FOR ... REMOVE_ALL - Delete All Occurrences

What it does: Removes all instances of a specific value from an array.

Unique power: Clean up arrays by removing every occurrence of a value, regardless of position.

Syntax:

FOR <array> REMOVE_ALL <value>

Example:

yaml
FOR tags REMOVE_ALL "draft"

Before:

yaml
tags: [draft, project, draft, active, draft]

After:

yaml
tags: [project, active]

FOR ... REMOVE_ANY - Delete Multiple Values

What it does: Removes all instances of any value in a provided list.

Unique power: Clean up multiple unwanted values in a single operation.

Syntax:

FOR <array> REMOVE_ANY [<value1>, <value2>, ...]

Example that can't be done any other way:

Clean up temporary tags in one operation:

yaml
FOR tags REMOVE_ANY ["draft", "wip", "temp"]

Before:

yaml
tags: [project, draft, active, wip, important, temp]

After:

yaml
tags: [project, active, important]

Why you need REMOVE_ANY: REMOVE_ALL requires multiple rules for multiple values. Only REMOVE_ANY removes multiple values at once.


DELETE - Remove Entire Field

What it does: Removes a field completely from the front matter.

Unique power: Structural cleanup that doesn't care about the field's value or type. Perfect for removing obsolete fields.

Syntax:

DELETE <field>

Example that can't be done any other way:

Clean up a deprecated field regardless of its value:

yaml
DELETE deprecated_field

Before:

yaml
---
title: My Note
status: active
deprecated_field: {complex: "object", with: ["nested", "data"]}
old_version: 1.0
---

After:

yaml
---
title: My Note
status: active
old_version: 1.0
---

Why you need DELETE: REMOVE only works on array values. SET updates values, doesn't remove fields. Only DELETE performs structural cleanup.


RENAME - Rename Field Preserving Values

What it does: Renames a field to a new name while preserving each document's unique value.

Unique power: This is an atomic operation that cannot be replicated with other operations. You cannot do this with DELETE + SET because each document has different values.

Syntax:

RENAME <old_field> TO <new_field>

Example that can't be done any other way:

Standardize "author" to "creator" across your entire vault:

yaml
RENAME author TO creator

Before (3 different documents):

yaml
# Doc 1: author: "Alice"
# Doc 2: author: "Bob"
# Doc 3: author: "Charlie"

After:

yaml
# Doc 1: creator: "Alice"  ✓ Value preserved
# Doc 2: creator: "Bob"    ✓ Value preserved
# Doc 3: creator: "Charlie" ✓ Value preserved

Why DELETE + SET won't work:

❌ DELETE author, SET creator "Alice"
   Problem: Sets ALL docs to "Alice", loses "Bob" and "Charlie"

❌ DELETE author, SET creator ???
   Problem: No way to reference the old value

Other examples:

RENAME due_date TO deadline
RENAME tags TO categories
RENAME project_id TO id

Why you need RENAME: It's the only way to change field names while preserving unique values across all documents. Essential for vault-wide standardization.


REPLACE - Pattern Substitution

What it does: Find and replace patterns in string values using regular expressions.

Unique power: In-place transformation of string content using pattern matching. Essential for data cleanup and normalization.

Syntax:

REPLACE <field> /<regex>/ <replacement>

Example that can't be done any other way:

Strip title prefixes like "Dr. " or "Dr " from author names:

yaml
REPLACE author /^Dr\.?\s+// ""

Before:

yaml
---
title: Medical Research
author: Dr. Jane Smith
co_author: Dr Jane Doe
reviewer: Professor Bob Wilson
---

After:

yaml
---
title: Medical Research
author: Jane Smith         # ← "Dr. " removed
co_author: Jane Doe        # ← "Dr " removed
reviewer: Professor Bob Wilson  # ← Unchanged (no "Dr")
---

More examples:

REPLACE title /\s+/ "_"              # Spaces → underscores
REPLACE content /TODO: // "✓ "       # Mark TODOs complete
REPLACE filename /\.draft$// ""      # Remove .draft extension

Advanced Regex Patterns

Regex flags:

  • /pattern/i - Case-insensitive matching
  • /pattern/g - Global replace (all occurrences)
  • /pattern/ig - Both case-insensitive and global

Case-Insensitive Replacement:

yaml
# Strip any case variation of "draft"
REPLACE title /draft/i ""
# "My Draft" → "My "
# "My DRAFT" → "My "
# "My draft" → "My "

# Remove title regardless of case
REPLACE author /dr\.?\s+/i ""
# "Dr. Smith" → "Smith"
# "DR SMITH" → "SMITH"
# "dr smith" → "smith"

Capture Groups & Substitution:

yaml
# Extract year from filename
REPLACE title /^.*(\d{4}).*$/ "$1"
# "report-2024-final.md" → "2024"

# Reorder date format (YYYY-MM-DD → MM/DD/YYYY)
REPLACE date /(\d{4})-(\d{2})-(\d{2})/ "$2/$3/$1"
# "2025-01-15" → "01/15/2025"

# Swap first and last name
REPLACE author /^(\w+)\s+(\w+)$/ "$2, $1"
# "John Smith" → "Smith, John"

# Extract domain from email
REPLACE contact /@(.+)$/ "$1"
# "user@example.com" → "example.com"

# Format phone number
REPLACE phone /(\d{3})(\d{3})(\d{4})/ "($1) $2-$3"
# "5551234567" → "(555) 123-4567"

Whitespace Normalization:

yaml
# Collapse multiple spaces to single space
REPLACE content /\s+/g " "
# "Too   many    spaces" → "Too many spaces"

# Remove leading and trailing whitespace
REPLACE title /^\s+|\s+$/g ""
# "  My Title  " → "My Title"

# Remove all whitespace
REPLACE code /\s+/g ""
# "function   name ( )" → "functionname()"

# Normalize line breaks (convert to single spaces)
REPLACE description /[\r\n]+/g " "

Character Removal & Sanitization:

yaml
# Remove special characters (keep alphanumeric and spaces)
REPLACE filename /[^a-zA-Z0-9\s]/g ""
# "My-File_Name#2!" → "My File Name 2"

# Create URL-friendly slug
REPLACE slug /[^a-z0-9]+/ig "-"
# "My Article Title!" → "My-Article-Title-"

# Remove leading/trailing dashes
REPLACE slug /^-+|-+$/g ""
# "-my-slug-" → "my-slug"

# Chain operations in single action:
REPLACE slug /[^a-z0-9]+/ig "-", REPLACE slug /^-+|-+$/g ""
# "My Article!" → "My-Article"

Complex Pattern Matching:

yaml
# Extract first word only
REPLACE title /^(\w+).*$/ "$1"
# "Getting Started with YAML" → "Getting"

# Remove HTML tags
REPLACE content /<[^>]+>/g ""
# "Text <b>bold</b> more" → "Text bold more"

# Extract initials
REPLACE author /(\w)\w*\s+(\w)\w*/ "$1.$2."
# "John Smith" → "J.S."

# Remove duplicate words
REPLACE content /\b(\w+)\s+\1\b/g "$1"
# "the the quick brown brown fox" → "the quick brown fox"

# Convert markdown links to plain text
REPLACE content /\[([^\]]+)\]\([^)]+\)/g "$1"
# "[Click here](http://example.com)" → "Click here"

Real-World Scenarios:

yaml
# Normalize author names (remove titles, fix spacing)
REPLACE author /^\s+|\s+$/g "",
REPLACE author /(Dr|Prof|Mr|Mrs|Ms)\.?\s+/i "",
REPLACE author /\s+/g " "
# "  Dr. Jane   Smith  " → "Jane Smith"

# Clean up imported data
REPLACE description /[\r\n\t]+/g " ",
REPLACE description /\s+/g " ",
REPLACE description /^\s+|\s+$/g ""
# Removes tabs, line breaks, excess spaces, trims ends

# Convert file path to relative
REPLACE filepath /^.*\/((?:[^\/]+\/){2}[^\/]+)$/ "$1"
# "/Users/name/Documents/vault/folder/file.md" → "vault/folder/file.md"

# Sanitize for safe filenames
REPLACE filename /[<>:"|?*\/\\]/g "-",
REPLACE filename /\s+/g "_",
REPLACE filename /_{2,}/g "_"
# "My File: Version 2*" → "My_File-_Version_2-"

Use cases:

  • Data cleanup: Remove prefixes, suffixes, unwanted characters
  • Format conversion: Dates, phone numbers, names
  • Text normalization: Whitespace, case, special characters
  • Content extraction: Capture specific parts of strings
  • URL/filename sanitization: Create safe, readable slugs

Why you need REPLACE: SET would require knowing the exact current value. Manual string operations aren't possible. Only REPLACE provides regex-based transformation.


INCREMENT - Numeric Operations

What it does: Adds to numeric fields. Creates field with value 0 if missing.

Unique power: Atomic numeric updates for counters, scores, and quantities. Handles missing fields automatically.

Syntax:

INCREMENT <field> <amount>

Example that can't be done any other way:

Track view count across multiple rule executions:

yaml
INCREMENT view_count 1

Before:

yaml
---
title: Popular Article
views: 42
---

After (first increment):

yaml
---
title: Popular Article
views: 43
---

After (second increment):

yaml
---
title: Popular Article
views: 44
---

If field is missing:

yaml
---
title: New Article
---
# Running: INCREMENT views 1
---
title: New Article
views: 1  # ← Created with value 1 (0 + 1)
---

Other examples:

DECREMENT priority 1             # Decrement
INCREMENT score 10               # Add 10 points
INCREMENT completion_rate 5      # Increase by 5%

Why you need INCREMENT: SET requires reading current value first. String operations don't work on numbers. Only INCREMENT provides true numeric operations.


FOR ... INSERT - Insert at Position

What it does: Insert value at specific position in arrays or strings (concatenation).

Unique power: Single operation handles prepending, appending, and inserting at any position using special indices.

Syntax:

FOR <field> INSERT <value> AT <index>

Special indices:

  • 0 - Insert at start (prepend)
  • -1 - Insert at end (append)
  • Positive numbers - Insert at that position
  • Negative numbers - Count from end

Example 1: Build a processing log (append)

yaml
FOR notes INSERT "\n\n---\nProcessed on 2025-12-05" AT -1

Before:

yaml
---
title: Research Note
notes: "Initial draft completed.\nReady for review."
---

After:

yaml
---
title: Research Note
notes: "Initial draft completed.\nReady for review.\n\n---\nProcessed on 2025-12-05"
---

Example 2: Mark drafts with prefix (prepend)

yaml
FOR title INSERT "DRAFT: " AT 0

Before:

yaml
---
title: Important Article
status: draft
---

After:

yaml
---
title: DRAFT: Important Article
status: draft
---

Pro tip: Combine with conditions to avoid duplicates:

Condition: status = "draft" AND NOT title contains "DRAFT: "
Action: FOR title INSERT "DRAFT: " AT 0

Other examples:

FOR title INSERT " (UPDATED)" AT -1      # Append suffix
FOR author INSERT "Dr. " AT 0            # Prepend prefix
FOR tags INSERT "urgent" AT 0            # Array: insert at start
FOR tags INSERT "archived" AT -1         # Array: insert at end
FOR history INSERT "checkpoint" AT 5     # Array: insert at position 5

Why you need INSERT: Single operation replaces APPEND/PREPEND/INSERT_AT with consistent, intuitive syntax using familiar negative indices.


Array Operations

FOR ... DEDUPLICATE - Remove Duplicates

Purpose: Remove duplicate values from array

Syntax:

FOR <array> DEDUPLICATE

Examples:

FOR tags DEDUPLICATE
FOR categories DEDUPLICATE

FOR ... SORT - Sort Array

Purpose: Sort array in ascending or descending order

Syntax:

FOR <array> SORT [<order>]
FOR <array> SORT BY <field> [<order>]

Orders: ASC (ascending), DESC (descending). Default is ASC.

Examples:

FOR tags SORT ASC
FOR priorities SORT DESC
FOR tasks SORT BY priority DESC
FOR entries SORT BY date ASC

FOR ... MOVE - Move Item by Index

Purpose: Move item from one index to another

Syntax:

FOR <array> MOVE FROM <index> TO <index>

Examples:

FOR tags MOVE FROM 0 TO 5
FOR history MOVE FROM -1 TO 0

Array of Objects Operations

Operations for arrays containing structured objects:

yaml
tasks:
  - name: "Task 1"
    status: "pending"
    priority: 8
  - name: "Task 2"
    status: "done"
    priority: 5

These operations work inside arrays, targeting specific items based on their properties.

FOR ... WHERE ... SET - Update Items by Condition

Purpose: Update fields in objects matching condition

Syntax:

FOR <array> WHERE <condition> SET <field> <value> [, <field> <value>]

Unique power: Updates specific objects within arrays based on their properties. File-level operations can't target individual array items.

Example - Task reassignment:

Before:

yaml
tasks:
  - name: "Design mockups"
    assignee: "Alice"
  - name: "Write tests"
    assignee: "Bob"
  - name: "Deploy"
    assignee: "Bob"

Action:

yaml
FOR tasks WHERE assignee = "Bob" SET assignee "Alice"

After:

yaml
tasks:
  - name: "Design mockups"
    assignee: "Alice"     # ← Unchanged
  - name: "Write tests"
    assignee: "Alice"     # ← Changed
  - name: "Deploy"
    assignee: "Alice"     # ← Changed

More examples:

FOR tasks WHERE status = "pending" SET status "active", started "{{today}}"
FOR readingList WHERE currentPage = pages SET status "finished", completedDate "{{today}}"
FOR meetings WHERE status = "scheduled" AND attendees HAS "Bob" SET status "cancelled"

FOR ... WHERE ... MOVE - Move Items by Condition

Purpose: Move items matching condition to start, end, or relative position

Syntax:

FOR <array> WHERE <condition> MOVE TO <position>

Positions: START, END, AFTER <condition>, BEFORE <condition>

Unique power: Reorders items within an array based on their properties without full sorting. Preserves relative order of non-matching items.

Example - Prioritize unwatched movies:

Before:

yaml
watchlist:
  - title: "Inception"
    watched: true
  - title: "Interstellar"
    watched: false
  - title: "The Matrix"
    watched: true
  - title: "Arrival"
    watched: false

Action:

yaml
FOR watchlist WHERE watched = false MOVE TO START

After:

yaml
watchlist:
  - title: "Interstellar"    # ← Moved to top
    watched: false
  - title: "Arrival"          # ← Moved to top
    watched: false
  - title: "Inception"        # ← Stays below
    watched: true
  - title: "The Matrix"       # ← Stays below
    watched: true

More examples:

FOR tasks WHERE priority > 7 MOVE TO START
FOR items WHERE status = "done" MOVE TO END

Relative Positioning with AFTER/BEFORE

Move items to specific positions relative to other items in the array.

MOVE TO AFTER - Insert urgent task after planning:

Before:

yaml
tasks:
  - name: Planning meeting
    priority: normal
  - name: Implementation
    priority: normal
  - name: URGENT BUG FIX
    priority: critical
  - name: Testing
    priority: low

Action:

FOR tasks WHERE priority = "critical" MOVE TO AFTER name = "Planning meeting"

After:

yaml
tasks:
  - name: Planning meeting
    priority: normal
  - name: URGENT BUG FIX       # ← Moved here (after Planning)
    priority: critical
  - name: Implementation        # ← Pushed down
    priority: normal
  - name: Testing
    priority: low

MOVE TO BEFORE - Insert review before completion:

Before:

yaml
workflow:
  - step: Design
    status: done
  - step: Implementation
    status: done
  - step: Deployment
    status: pending

Action:

FOR workflow WHERE step = "Review" OR step = "Testing" MOVE TO BEFORE step = "Deployment"

If the "Review" step exists, it moves before "Deployment". If it doesn't exist, the action has no effect (safe to run).

Use cases:

  • Insert urgent items after planning but before execution
  • Group related items together without full sort
  • Maintain manual ordering with selective repositioning
  • Position new items relative to existing anchors

Object Operations

FOR ... MERGE - Deep Merge Objects

Purpose: Deep merge objects (preserves existing nested fields)

Syntax:

FOR <object> MERGE {<key>: <value>, ...}

Examples:

FOR metadata MERGE {author: "John", version: 2}
FOR config MERGE {theme: "dark", showIcons: true}

Nested objects are recursively merged. Arrays and primitives are replaced.

FOR ... MERGE_OVERWRITE - Shallow Merge Objects

Purpose: Shallow merge objects (overwrites all fields)

Syntax:

FOR <object> MERGE_OVERWRITE {<key>: <value>, ...}

Examples:

FOR metadata MERGE_OVERWRITE {author: "Jane", version: 3}
FOR settings MERGE_OVERWRITE {mode: "advanced"}

All fields from the new object replace existing fields at the top level only.


Special Values

Template Variables

Template variables are evaluated at runtime and inserted into action values:

  • - Current timestamp (ISO 8601 format)
  • - Today's date (YYYY-MM-DD)
yaml
SET created_date "{{now}}"
SET updated "{{today}}"
SET reviewed_on "{{today}}"

Data Types

Supported value types:

  • Strings: "text" (use quotes)
  • Numbers: 42, 3.14
  • Booleans: true, false
  • Null: null
  • Arrays: ["item1", "item2"]
  • Objects: {key: "value", count: 5}
  • Dates: "2025-01-01"

Multiple Actions

Execute multiple actions in one rule (comma-separated):

yaml
SET status "completed", SET completed_date "{{now}}", FOR tags APPEND "done"

Actions execute left-to-right in order.


Troubleshooting

Common issues and how to handle them.

Field Doesn't Exist

Problem: Action tries to modify non-existent field

Behavior:

  • SET field value - Creates the field ✅
  • FOR field APPEND "x" - Creates array ["x"]
  • INCREMENT field 1 - Error (can't increment missing field) ❌
  • REPLACE field /old/new - Error (can't replace in missing field) ❌

Solution: Use conditions to check existence first:

yaml
Condition: HAS views
Action: INCREMENT views 1

Or create field with SET first:

yaml
Action: SET views 0, INCREMENT views 1

Type Mismatch Errors

Problem: Operation expects different type

Examples:

yaml
# views is string "5"
INCREMENT views 1
→ Error: Cannot increment string

# tags is string "old"
FOR tags APPEND "new"
→ Converts to array: ["old", "new"] 

# config is array
FOR config MERGE {key: "value"}
→ Error: Cannot merge with array

Solution: Use type checking conditions:

yaml
Condition: views :number
Action: INCREMENT views 1

Or normalize types first:

yaml
Condition: tags !:array
Action: SET tags ["{{fm:tags}}"]  # Convert to array

Empty vs Null vs Missing

Understanding the differences:

Statefield existsfield emptyfield :nullFOR field APPEND "x"
Not presentfalsefalsefalseCreates ["x"]
field: nulltruefalsetrueReplaces with ["x"]
field: []truetruefalseResults in ["x"]
field: ""truetruefalseError (can't append to string)

Best practices:

yaml
# Check if field has content
Condition: tags exists AND tags !empty

# Check for null specifically
Condition: deletedAt :null

# Safe append (creates if missing)
Action: FOR tags APPEND "new"

No Matches in WHERE

Problem: WHERE condition finds no items

Behavior: Operation succeeds but does nothing (not an error)

Example:

yaml
FOR tasks WHERE status = "archived" REMOVE
# No tasks are archived → No change, no error ✅

Checking for matches:

yaml
Condition: ANY tasks WHERE status = "archived"
Action: FOR tasks WHERE status = "archived" REMOVE

Array Index Out of Bounds

Problem: Accessing non-existent index

Behavior:

yaml
# tags has 3 items
tasks[100].status = "done"
→ Condition is false (index doesn't exist)

FOR tags INSERT "x" AT 50
→ Inserts at end (safe fallback)

FOR tags INSERT "x" AT -50
→ Inserts at start (safe fallback)

Tip: Negative indexing is safer for "last item" access:

yaml
# Better (works regardless of array length)
tasks[-1].status = "done"

# Worse (requires knowing exact length)
tasks[4].status = "done"

Deep Path Missing

Problem: Intermediate path doesn't exist

Behavior:

yaml
# config.server doesn't exist
config.server.database.host = "localhost"
→ Condition is false (path broken at config.server)

Solution: Check existence step by step:

yaml
Condition: HAS config AND HAS config.server
Action: SET config.server.database.host "localhost"

Or create paths explicitly:

yaml
Action: SET config.server {database: {host: "localhost"}}

Operations That Won't Fail

Always safe (return true/false, never error):

  • HAS field
  • field exists
  • field empty
  • field :type
  • ANY array WHERE ...
  • ALL array WHERE ...

May fail (require correct types):

  • INCREMENT/DECREMENT - needs number
  • REPLACE - needs string
  • FOR ... MERGE - needs object

Examples

See Examples for real-world usage patterns.

Released under the MIT License.