Plugins
Extend nosh with custom prompt variables
Plugins
Plugins add dynamic variables to your prompt. They can show git info, execution time, language versions, or anything you can get from a shell command.
How Plugins Work
Plugins are TOML files that define:
- Variables - Named values that can be used in themes
- Commands - Shell commands to get the values
- Transforms - How to process the output
When your prompt renders, nosh fetches all plugin values in parallel with smart timeout handling:
- Parallel execution - All plugin commands run concurrently, not one after another
- Soft timeout (100ms) - If a command doesn't finish in time, nosh uses the cached value and keeps running the command in the background
- Background continuation - Slow commands keep running after the prompt appears, updating the cache for next time
- Hard timeout (5s) - Commands that take too long are eventually terminated
- No duplicate runs - If a plugin is still running from the previous prompt, nosh won't start it again
This means your prompt stays fast even with slow plugins. The first time you enter a directory, a slow plugin might show nothing (empty cache), but by the next prompt it will have the value ready.
Built-in Plugins
nosh comes with three plugins in packages/builtins/plugins/:
builtins/context
The most useful plugin. It detects your project context without running slow shell commands:
- Git info: branch name, dirty status
- Language versions: Rust, Node.js, Python, Go, Bun, Docker
- Package info: name and version from package.json, Cargo.toml, etc.
# In your theme
format = "[{builtins/context:git_branch}](purple) [{builtins/context:rust_version}](red)"
[plugins]
"builtins/context" = { enabled = true }
All context variables:
| Variable | What it shows |
|---|---|
git_branch | Current branch name |
git_status | * if uncommitted changes |
rust_version | Rust toolchain (e.g., 1.75.0) |
rust_icon | 🦀 when in a Rust project |
node_version | Node.js version |
node_icon | ⬢ when in a Node project |
python_version | Python version |
python_icon | 🐍 when in a Python project |
go_version | Go version |
go_icon | 🐹 when in a Go project |
bun_version | Bun version |
bun_icon | 🥟 when in a Bun project |
docker_version | Docker version |
docker_icon | 🐳 when Docker is available |
package_name | Package name from manifest |
package_version | Package version |
package_icon | 📦 when in a package |
builtins/exec_time
Shows how long commands take to run:
format = "[{builtins/exec_time:took}](yellow)"
[plugins]
"builtins/exec_time" = { enabled = true, min_ms = 500 }
The min_ms option hides the time for fast commands. Set it to 500 to only show for commands taking over half a second.
| Variable | Example |
|---|---|
took | took 2.3s |
duration | 2.3s |
builtins/git
Git info via shell commands (alternative to context plugin):
format = "[{builtins/git:branch}](purple){builtins/git:dirty}"
| Variable | Description |
|---|---|
branch | Current branch name |
dirty | Shows * if there are changes |
Creating Your Own Plugin
Step 1: Create the file
Run /create and select "Plugin", or create manually:
~/.config/nosh/packages/local/plugins/myplugin.toml
Step 2: Write the plugin
Here's a simple example that shows the current time:
[plugin]
name = "clock"
description = "Current time"
[provides]
time = { command = "date +%H:%M" }
Step 3: Use it in your theme
format = "[{local/clock:time}](cyan) [{dir}](blue) $ "
Plugin Format Reference
Basic structure
[plugin]
name = "myplugin" # Required: unique identifier
description = "Description" # Optional: what it does
[provides]
# Define your variables here
[icons]
# Icons for transforms (optional)
[config]
# Plugin-specific settings (optional)
Command-based variables
Run a shell command and use the output:
[provides]
# Simple command
branch = { command = "git branch --show-current 2>/dev/null" }
# Command with error suppression
uptime = { command = "uptime | awk '{print $3}' 2>/dev/null" }
Transform-based variables
Process the command output:
[provides]
dirty = { command = "git status --porcelain", transform = "non_empty" }
[icons]
dirty = "*" # Shown when command has output
clean = "" # Shown when command output is empty
Available transforms:
| Transform | What it does |
|---|---|
non_empty | Returns icon based on whether there's output (uses dirty/clean icons) |
with_icon | Prepends the variable's icon when value exists; hides entirely if empty |
trim | Removes leading/trailing whitespace |
Using with_icon:
[provides]
temp = { command = "curl -s 'wttr.in?format=%t'", transform = "with_icon" }
[icons]
temp = "🌡️" # Icon name must match variable name
When the command returns +5°C, the output is 🌡️ +5°C. When empty or timed out, nothing is shown.
Timeout and Cache Settings
Control how long nosh waits for a command and how long results are cached:
[provides]
# Fast command - wait up to 50ms, cache for 1 second
fast_var = { command = "echo hello", timeout = "50ms", cache = "1s" }
# Slow API - don't wait at all (fully async), cache for 5 minutes
weather = { command = "curl -s 'wttr.in?format=%t'", timeout = "0", cache = "5m" }
# Volatile data - always fetch fresh (no caching)
time = { command = "date +%H:%M:%S", cache = "always" }
# Static data - cache forever (only fetch once)
hostname = { command = "hostname", cache = "never" }
Timeout values:
| Value | What happens |
|---|---|
"0" | Fully async - show cached value immediately, fetch in background |
"100ms" | Wait up to 100ms for result (default) |
"1s" | Wait up to 1 second |
Cache values:
| Value | What happens |
|---|---|
"always" | Always fetch fresh - no caching |
"never" | Cache forever - only fetch once per session |
"500ms" | Cache for 500 milliseconds (default) |
"10s" | Cache for 10 seconds |
"5m" | Cache for 5 minutes |
"1h" | Cache for 1 hour |
Use timeout = "0" with cache = "5m" for slow API calls like weather - the first prompt won't show the value, but subsequent prompts will use the cached result while fetching updates in the background.
Internal variables
Use nosh's built-in providers (faster than shell commands):
[provides]
git_branch = { source = "internal" }
Example Plugins
Weather
[plugin]
name = "weather"
description = "Current weather from wttr.in"
[provides]
temp = { command = "curl -s 'wttr.in?format=%t' 2>/dev/null", transform = "with_icon" }
condition = { command = "curl -s 'wttr.in?format=%C' 2>/dev/null" }
[icons]
temp = "🌡️"
Use with_icon to show 🌡️ +5°C when available, or nothing when the API times out.
Battery (macOS)
[plugin]
name = "battery"
description = "Battery percentage"
[provides]
percent = { command = "pmset -g batt | grep -o '[0-9]*%' | head -1" }
charging = { command = "pmset -g batt | grep -q 'AC Power' && echo '⚡' || echo ''" }
Kubernetes Context
[plugin]
name = "k8s"
description = "Kubernetes context and namespace"
[provides]
context = { command = "kubectl config current-context 2>/dev/null" }
namespace = { command = "kubectl config view --minify -o jsonpath='{..namespace}' 2>/dev/null" }
Docker Containers
[plugin]
name = "docker"
description = "Running Docker containers"
[provides]
count = { command = "docker ps -q 2>/dev/null | wc -l | tr -d ' '" }
running = { command = "docker ps --format '{{.Names}}' 2>/dev/null | head -1" }
Plugin Naming
How you reference a plugin depends on where it's from:
| Source | Location | Usage in theme |
|---|---|---|
| Built-in | packages/builtins/plugins/ | {builtins/plugin:var} |
| Your own | packages/local/plugins/ | {local/plugin:var} |
| Package | packages/name/plugins/ | {name/plugin:var} |
Enabling Plugins in Themes
Every plugin you use must be enabled in your theme:
[plugins]
"builtins/context" = { enabled = true }
"builtins/exec_time" = { enabled = true, min_ms = 1000 }
"weather" = { enabled = true }
"k8s" = { enabled = false } # Disabled
Performance Tips
-
Use
builtins/contextinstead of shell commands - It's much faster because it doesn't spawn processes. Context variables are computed synchronously and never delay your prompt. -
Use
timeout = "0"for slow commands - API calls or slow commands should usetimeout = "0"to avoid delaying your prompt. The first time shows nothing, but subsequent prompts show cached values while fetching in the background. -
Cache aggressively for stable data - Use
cache = "5m"orcache = "never"for data that doesn't change often (weather, hostname, k8s context). This reduces unnecessary command execution. -
Use
cache = "always"sparingly - Only for truly volatile data that must be fresh every prompt. This causes a command to run on every prompt. -
Keep commands under 100ms when possible - Faster commands mean immediately visible data instead of cached values. Use
2>/dev/nullto suppress error output and speed up failing commands. -
Disable plugins you don't use - Set
enabled = falseto skip them entirely. -
Background execution limits - If a command takes more than 5 seconds, it's killed to prevent resource buildup. Design your plugins to complete within a reasonable time.
Installing Plugin Packages
Get plugins made by others:
/install user/repo # Install from GitHub
/sync # Update all packages
/packages # Manage packages
Then enable in your theme:
[plugins]
"repo-name/plugin-name" = { enabled = true }