- TypeScript 99.5%
- Dockerfile 0.3%
- Shell 0.2%
| .zed | ||
| example | ||
| src | ||
| tests | ||
| .gitattributes | ||
| .gitignore | ||
| AGENTS.md | ||
| deno.json | ||
| main.js | ||
| main.ts | ||
| README.md | ||
| release.ts | ||
Vish
A lightweight alternative to Ansible.
Install
deno install -f -A -g https://vski.sh/x/vish/raw/tag/v1/main.js --name=vish
Or build binary:
deno task build # creates ./vish
Quick Start
# Bootstrap a new project
vish create project my-infra
cd my-infra
vish create playbook deploy
vish create action pull playbooks/deploy
# Run actions
vish prod1 deploy pull # Run action on server
vish group:production deploy pull # Run on server group
vish prod1 deploy pull --dry-run # Preview commands
vish prod1 deploy pull --docker # Test in Docker container
vish test --docker # Run all tests in Docker
Project Structure
project/
├── .vish.yaml # project config
├── .env # environment variables
├── .vish.secrets.yaml # encrypted secrets
├── test.Dockerfile # custom Docker image for testing (optional)
└── playbooks/
└── deploy/
├── .vish.yaml # playbook config (optional dependencies)
├── check.yaml
├── pull.yaml
├── build.yaml
├── restart.yaml
└── scripts/ # uploaded files (optional)
└── build.sh
Config
.vish.yaml:
servers:
# Simple format: user@host[:port]
prod1: root@192.168.1.10
prod2: root@192.168.1.11
# Extended format with explicit SSH options
staging:
host: staging.example.com
user: deploy
port: 2222
identity_file: ~/.ssh/id_rsa_staging
bastion:
host: bastion.example.com
user: admin
ssh_options:
StrictHostKeyChecking: 'no'
UserKnownHostsFile: /dev/null
groups:
production: [prod1, prod2]
playbooks:
- ./playbooks/deploy
- git@github.com:org/shared-playbooks.git # remote playbook
# Custom Docker image for testing
docker:
dockerfile: test.Dockerfile
context: .
Server options:
| Field | Description |
|---|---|
host |
Hostname or IP address |
user |
SSH username (default: root) |
port |
SSH port (default: 22) |
identity_file |
Path to SSH private key |
ssh_options |
Additional SSH options (key/value) |
Docker options:
| Field | Description |
|---|---|
dockerfile |
Path to custom Dockerfile |
context |
Build context directory (default: .) |
Uses ~/.ssh/config as fallback for settings not specified in .vish.yaml.
Actions
playbooks/deploy/pull.yaml:
name: pull
description: Pull latest code
depends_on:
- check
env:
BRANCH: main
GIT_REPO: /var/www/app
command: |
cd $GIT_REPO
git pull origin $BRANCH
Env priority: action < .env < CLI --env < secrets
File Uploads
Upload files/directories before action execution:
name: build
description: Build application
files:
- scripts/*.sh
- config/*.json
env:
APP_DIR: /var/www/app
command: |
chmod +x $VISH_FILES_DIR/scripts/*.sh
$VISH_FILES_DIR/scripts/build.sh
files: Glob patterns relative to playbook directory- Uploaded files are available in
$VISH_FILES_DIRon the server - Directories are created automatically
Dependencies
Define pre-execution checks with auto-fix capability:
name: check
description: Check dependencies
dependencies:
- check: git --version
ensure: apk add --no-cache git
- check: curl --version
ensure: apk add --no-cache curl
command: echo "All dependencies satisfied"
check: Command that exits 0 if dependency existsensure: Command to install/fix if check fails
Dependencies are verified before actions execute on each server. If check
fails, ensure runs automatically, then re-checks.
Playbook-level dependencies can be defined in playbooks/deploy/.vish.yaml:
type: playbook
name: deploy
dependencies:
- check: which node
ensure: apk add --no-cache nodejs
actions:
- ./check.yaml
- ./build.yaml
Project and playbook dependencies are merged at runtime.
Secrets
vish secrets encrypt # encrypt .vish.secrets.yaml
vish secrets decrypt # print decrypted to stdout
Password via VISH_SECRETS_PASSWORD env var.
Auto-encrypt on commit:
vish hooks install
Commands
| Command | Description |
|---|---|
vish create project [path] |
Create project (default: ./) |
vish create playbook <name> [path] |
Create playbook |
vish create action <name> [path] |
Create action |
vish list servers |
List servers and groups |
vish list playbooks |
List playbooks |
vish list actions deploy |
List actions in playbook |
vish sync |
Pull remote playbooks from git |
vish test --docker |
Run tests in Docker containers |
vish hooks install |
Install pre-commit hook |
Bootstrap
Generate project scaffolding with templates:
# Create new project
vish create project ./my-infra
# Create playbook (from project dir)
vish create playbook deploy
# Create action (from playbook dir or with path)
vish create action restart
vish create action build ./playbooks/deploy
Generated templates include commented examples for servers, groups, dependencies, and commands.
Flags
| Flag | Description |
|---|---|
--dry-run |
Preview without execution |
--docker |
Run in Docker containers |
--env KEY=VAL |
Set env var (repeatable) |
--check |
Check if secrets need encryption |
--non-interactive |
Use env var for password |
--json |
Output JSON events (for piping) |
Output
By default, vish outputs human-readable text with colors and status indicators:
▶ Running "pull" on 2 server(s)
Servers: prod1, prod2
[prod1] connecting...
→ check
✓ git --version
→ pull
Already up to date.
✓ pull
[prod1] done
[prod2] connecting...
→ check
✓ git --version
→ pull
Already up to date.
✓ pull
[prod2] done
✓ All servers succeeded
2 succeeded, 0 failed
For scripting or piping to tools like jq, use --json:
vish prod1 deploy pull --json | jq -c .
JSON output (one event per line):
{"type":"run_start","servers":["prod1"],"playbook":"deploy","action":"pull"}
{"type":"server_start","server":"prod1"}
{"type":"ensure_start","server":"prod1","check":"git --version"}
{"type":"ensure_check_pass","server":"prod1","check":"git --version"}
{"type":"action_start","server":"prod1","action":"pull"}
{"type":"stdout","server":"prod1","data":"Already up to date.\n"}
{"type":"action_end","server":"prod1","action":"pull","exitCode":0}
{"type":"server_end","server":"prod1","success":true}
{"type":"run_end","results":{"prod1":{"success":true,"exitCode":0}}}
MCP Server
Vish includes an MCP (Model Context Protocol) server for AI agent integration. This allows AI assistants to execute vish commands and track their status asynchronously.
Starting the Server
vish server --project /path/to/project
The server uses stdio transport, making it compatible with MCP clients like Claude Desktop.
Available Tools
| Tool | Description |
|---|---|
list_servers |
List all servers and groups in the project |
list_playbooks |
List all available playbooks |
list_actions |
List actions in a specific playbook |
execute |
Execute an action on servers (async) |
get_status |
Get execution status and results |
get_logs |
Get execution logs with pagination |
cancel |
Cancel a running execution |
Execution Model
The execute tool returns immediately with an execution_id. Use get_status
to poll for completion and get_logs to retrieve output:
// Execute action
{"execution_id": "abc123", "status": "running"}
// Check status
{"execution_id": "abc123", "status": "completed", "success": true}
// Get logs
{"logs": [...], "cursor": "token", "has_more": false}
Claude Desktop Configuration
Add to your Claude Desktop config:
{
"mcpServers": {
"vish": {
"command": "vish",
"args": ["server", "--project", "/path/to/your/project"]
}
}
}