diff --git a/model_tools.py b/model_tools.py
index c8682dacb..cc2cea9c9 100644
--- a/model_tools.py
+++ b/model_tools.py
@@ -95,6 +95,7 @@ def _discover_tools():
"tools.send_message_tool",
"tools.honcho_tools",
"tools.homeassistant_tool",
+ "tools.infsh_tool",
]
import importlib
for mod_name in _modules:
diff --git a/skills/inference-sh/DESCRIPTION.md b/skills/inference-sh/DESCRIPTION.md
new file mode 100644
index 000000000..a4b2aa91c
--- /dev/null
+++ b/skills/inference-sh/DESCRIPTION.md
@@ -0,0 +1,23 @@
+# inference.sh
+
+Run 150+ AI applications in the cloud via the [inference.sh](https://inference.sh) platform.
+
+**One API key for everything** - Access all AI services with a single account. No need to manage separate API keys for FLUX, Veo, Claude, Tavily, Twitter, etc. You can also bring your own keys if you prefer.
+
+## Available Skills
+
+- **cli**: The inference.sh CLI (`infsh`) for running AI apps
+
+## What's Included
+
+- **Image Generation**: FLUX, Reve, Seedream, Grok Imagine, Gemini
+- **Video Generation**: Veo, Wan, Seedance, OmniHuman, HunyuanVideo
+- **LLMs**: Claude, Gemini, Kimi, GLM-4 (via OpenRouter)
+- **Search**: Tavily, Exa
+- **3D**: Rodin
+- **Social**: Twitter/X automation
+- **Audio**: TTS, voice cloning
+
+## Tools
+
+This category provides the `infsh` and `infsh_install` tools in the `inference` toolset.
diff --git a/skills/inference-sh/cli/SKILL.md b/skills/inference-sh/cli/SKILL.md
new file mode 100644
index 000000000..67e33436e
--- /dev/null
+++ b/skills/inference-sh/cli/SKILL.md
@@ -0,0 +1,372 @@
+---
+name: inference-sh-cli
+description: "Run 150+ AI apps via inference.sh CLI (infsh) - image generation, video creation, LLMs, search, 3D, Twitter automation. Models: FLUX, Veo, Gemini, Grok, Claude, Seedance, OmniHuman, Tavily, Exa, OpenRouter. Triggers: inference.sh, infsh, ai apps, serverless ai, flux, veo, image generation, video generation"
+version: 1.0.0
+author: inference.sh
+license: MIT
+metadata:
+ hermes:
+ tags: [AI, image-generation, video, LLM, search, inference, FLUX, Veo, Claude]
+ requires_tools: [infsh]
+---
+
+# inference.sh CLI
+
+Run 150+ AI apps in the cloud with a simple CLI. No GPU required.
+
+**One API key for everything** - Manage all AI services (FLUX, Veo, Claude, Tavily, X/Twitter, and more) with a single inference.sh account. No need to sign up for dozens of different providers. You can also bring your own API keys if you prefer.
+
+## Tools
+
+This skill is backed by the `infsh` and `infsh_install` tools:
+
+- **infsh**: Run any infsh command (app list, app run, etc.)
+- **infsh_install**: Install the CLI if not already present
+
+## Quick Start
+
+```bash
+# Install (if needed)
+infsh_install
+
+# List available apps
+infsh app list
+
+# Search for apps
+infsh app list --search flux
+infsh app list --search video
+
+# Run an app
+infsh app run falai/flux-dev-lora --input '{"prompt": "a cat astronaut"}' --json
+```
+
+## Local File Uploads
+
+The CLI automatically uploads local files when you provide a file path instead of a URL:
+
+```bash
+# Upscale a local image
+infsh app run falai/topaz-image-upscaler --input '{"image": "/path/to/photo.jpg", "upscale_factor": 2}' --json
+
+# Image-to-video from local file
+infsh app run falai/wan-2-5-i2v --input '{"image": "/path/to/image.png", "prompt": "make it come alive"}' --json
+
+# Video generation with local first frame
+infsh app run bytedance/seedance-1-5-pro --input '{"prompt": "dancing figure", "image": "./first-frame.png"}' --json
+```
+
+## Image Generation
+
+```bash
+# Gemini 2.5 Flash Image (Google) - fast, high quality
+infsh app run google/gemini-2-5-flash-image --input '{"prompt": "futuristic city", "num_images": 1}' --json
+
+# Gemini 3 Pro Image Preview (Google) - latest model
+infsh app run google/gemini-3-pro-image-preview --input '{"prompt": "photorealistic landscape"}' --json
+
+# Gemini 3.1 Flash Image Preview (Google)
+infsh app run google/gemini-3-1-flash-image-preview --input '{"prompt": "artistic portrait"}' --json
+
+# FLUX Dev with LoRA support
+infsh app run falai/flux-dev-lora --input '{"prompt": "sunset over mountains", "num_images": 1}' --json
+
+# FLUX 2 Klein with LoRA
+infsh app run falai/flux-2-klein-lora --input '{"prompt": "portrait photo"}' --json
+
+# Reve - stylized generation and editing
+infsh app run falai/reve --input '{"prompt": "cyberpunk city"}' --json
+
+# Seedream 5 Lite - high-quality 2K-3K (ByteDance)
+infsh app run bytedance/seedream-5-lite --input '{"prompt": "nature scene"}' --json
+
+# Seedream 4.5 - 2K-4K images
+infsh app run bytedance/seedream-4-5 --input '{"prompt": "detailed illustration"}' --json
+
+# Seedream 3.0 - cinematic quality
+infsh app run bytedance/seedream-3-0-t2i --input '{"prompt": "fantasy landscape"}' --json
+
+# Grok Imagine - xAI image generation
+infsh app run xai/grok-imagine-image --input '{"prompt": "abstract art"}' --json
+
+# Grok Imagine Pro - higher quality
+infsh app run xai/grok-imagine-image-pro --input '{"prompt": "photorealistic portrait"}' --json
+
+# Qwen Image 2 Pro (Alibaba)
+infsh app run alibaba/qwen-image-2-pro --input '{"prompt": "anime character"}' --json
+```
+
+## Video Generation
+
+```bash
+# Veo 3.1 Fast (Google)
+infsh app run google/veo-3-1-fast --input '{"prompt": "drone shot of coastline"}' --json
+
+# Veo 3.1 (higher quality)
+infsh app run google/veo-3-1 --input '{"prompt": "cinematic scene"}' --json
+
+# Veo 3 Fast
+infsh app run google/veo-3-fast --input '{"prompt": "nature documentary shot"}' --json
+
+# Veo 2
+infsh app run google/veo-2 --input '{"prompt": "slow motion water splash"}' --json
+
+# Grok Imagine Video - xAI
+infsh app run xai/grok-imagine-video --input '{"prompt": "timelapse of clouds"}' --json
+
+# Seedance 1.5 Pro - ByteDance
+infsh app run bytedance/seedance-1-5-pro --input '{"prompt": "dancing figure", "resolution": "1080p"}' --json
+
+# Seedance 1.0 Pro
+infsh app run bytedance/seedance-1-0-pro --input '{"prompt": "walking through forest"}' --json
+
+# Seedance 1.0 Pro Fast
+infsh app run bytedance/seedance-1-0-pro-fast --input '{"prompt": "quick motion"}' --json
+
+# Seedance 1.0 Lite - 720p lightweight
+infsh app run bytedance/seedance-1-0-lite --input '{"prompt": "simple animation"}' --json
+
+# Wan 2.5 - text-to-video
+infsh app run falai/wan-2-5 --input '{"prompt": "person walking through city"}' --json
+
+# Wan 2.5 Image-to-Video
+infsh app run falai/wan-2-5-i2v --input '{"image": "/path/to/image.png", "prompt": "make it move naturally"}' --json
+
+# LTX Video
+infsh app run infsh/ltx-video --input '{"prompt": "realistic scene"}' --json
+
+# Magi 1
+infsh app run infsh/magi-1 --input '{"prompt": "creative video"}' --json
+```
+
+## Avatar & Lipsync
+
+```bash
+# OmniHuman 1.5 - multi-character audio-driven avatars
+infsh app run bytedance/omnihuman-1-5 --input '{"audio": "/path/to/audio.mp3", "image": "/path/to/face.jpg"}' --json
+
+# OmniHuman 1.0
+infsh app run bytedance/omnihuman-1-0 --input '{"audio": "/path/to/speech.wav", "image": "/path/to/portrait.png"}' --json
+
+# Fabric 1.0 - image animation
+infsh app run falai/fabric-1-0 --input '{"image": "/path/to/photo.jpg"}' --json
+
+# PixVerse Lipsync
+infsh app run falai/pixverse-lipsync --input '{"audio": "/path/to/audio.mp3", "video": "/path/to/video.mp4"}' --json
+```
+
+## Upscaling
+
+```bash
+# Topaz Image Upscaler - up to 4x
+infsh app run falai/topaz-image-upscaler --input '{"image": "/path/to/photo.jpg", "upscale_factor": 2}' --json
+
+# Topaz Video Upscaler
+infsh app run falai/topaz-video-upscaler --input '{"video": "/path/to/video.mp4"}' --json
+
+# Real-ESRGAN - image enhancement
+infsh app run infsh/real-esrgan --input '{"image": "/path/to/image.jpg"}' --json
+
+# Thera - upscale to any size
+infsh app run infsh/thera --input '{"image": "/path/to/image.jpg"}' --json
+```
+
+## LLMs (via OpenRouter)
+
+```bash
+# Claude Opus 4.6
+infsh app run openrouter/claude-opus-46 --input '{"prompt": "Explain quantum computing"}' --json
+
+# Claude Sonnet 4.5
+infsh app run openrouter/claude-sonnet-45 --input '{"prompt": "Write a poem"}' --json
+
+# Claude Haiku 4.5
+infsh app run openrouter/claude-haiku-45 --input '{"prompt": "Quick question"}' --json
+
+# Gemini 3 Pro Preview
+infsh app run openrouter/gemini-3-pro-preview --input '{"prompt": "Analyze this"}' --json
+
+# Kimi K2 Thinking
+infsh app run openrouter/kimi-k2-thinking --input '{"prompt": "Solve this step by step"}' --json
+
+# GLM 4.6
+infsh app run openrouter/glm-46 --input '{"prompt": "Help me with"}' --json
+
+# MiniMax M2.5
+infsh app run openrouter/minimax-m-25 --input '{"prompt": "Creative writing"}' --json
+
+# Intellect 3
+infsh app run openrouter/intellect-3 --input '{"prompt": "Research question"}' --json
+```
+
+## Web Search
+
+```bash
+# Tavily Search Assistant - comprehensive results
+infsh app run tavily/search-assistant --input '{"query": "latest AI news", "include_answer": true}' --json
+
+# Tavily Extract - get content from URLs
+infsh app run tavily/extract --input '{"urls": ["https://example.com"]}' --json
+
+# Exa Search - neural search
+infsh app run exa/search --input '{"query": "machine learning tutorials"}' --json
+
+# Exa Answer - LLM-powered answers
+infsh app run exa/answer --input '{"query": "what is transformers architecture"}' --json
+
+# Exa Extract - extract web content
+infsh app run exa/extract --input '{"url": "https://example.com"}' --json
+```
+
+## 3D Generation
+
+```bash
+# Rodin 3D Generator
+infsh app run infsh/rodin-3d-generator --input '{"prompt": "a wooden chair"}' --json
+
+# HunyuanImage to 3D
+infsh app run infsh/hunyuan-image-to-3d-2 --input '{"image": "/path/to/object.png"}' --json
+```
+
+## Text-to-Speech
+
+```bash
+# Kokoro TTS - lightweight
+infsh app run falai/kokoro-tts --input '{"text": "Hello, this is a test."}' --json
+
+# Dia TTS - realistic dialogue
+infsh app run falai/dia-tts --input '{"text": "Two characters talking"}' --json
+```
+
+## Twitter/X Automation
+
+```bash
+# Post a tweet
+infsh app run x/post-tweet --input '{"text": "Hello from AI!"}' --json
+
+# Create post with media
+infsh app run x/post-create --input '{"text": "Check this out", "media": "/path/to/image.jpg"}' --json
+
+# Send DM
+infsh app run x/dm-send --input '{"recipient_id": "123456", "text": "Hi there!"}' --json
+
+# Follow user
+infsh app run x/user-follow --input '{"user_id": "123456"}' --json
+
+# Like a post
+infsh app run x/post-like --input '{"post_id": "123456789"}' --json
+
+# Retweet
+infsh app run x/post-retweet --input '{"post_id": "123456789"}' --json
+
+# Get user profile
+infsh app run x/user-get --input '{"username": "elonmusk"}' --json
+
+# Get post
+infsh app run x/post-get --input '{"post_id": "123456789"}' --json
+
+# Delete post
+infsh app run x/post-delete --input '{"post_id": "123456789"}' --json
+```
+
+## Utilities
+
+```bash
+# Browser automation
+infsh app run infsh/agent-browser --function open --session new --input '{"url": "https://example.com"}' --json
+
+# Media merger - combine videos/images
+infsh app run infsh/media-merger --input '{"files": ["/path/to/video1.mp4", "/path/to/video2.mp4"]}' --json
+
+# Video audio extractor
+infsh app run infsh/video-audio-extractor --input '{"video": "/path/to/video.mp4"}' --json
+
+# Video audio merger
+infsh app run infsh/video-audio-merger --input '{"video": "/path/to/video.mp4", "audio": "/path/to/audio.mp3"}' --json
+
+# Caption videos
+infsh app run infsh/caption-videos --input '{"video": "/path/to/video.mp4"}' --json
+
+# Stitch images
+infsh app run infsh/stitch-images --input '{"images": ["/path/to/1.jpg", "/path/to/2.jpg"]}' --json
+
+# Python executor
+infsh app run infsh/python-executor --input '{"code": "print(2+2)"}' --json
+
+# HTML to image
+infsh app run infsh/html-to-image --input '{"html": "
Hello
"}' --json
+
+# NSFW detection
+infsh app run infsh/falconsai-nsfw-detection --input '{"image": "/path/to/image.jpg"}' --json
+
+# Media analyzer
+infsh app run infsh/media-analyzer --input '{"file": "/path/to/media.jpg"}' --json
+```
+
+## Common Patterns
+
+### Generate + Upscale Pipeline
+
+```bash
+# Generate image, capture URL, then upscale
+infsh app run falai/flux-dev-lora --input '{"prompt": "portrait photo"}' --json --save result.json
+
+# Extract URL and upscale (using jq)
+IMG=$(cat result.json | jq -r '.images[0].url')
+infsh app run falai/topaz-image-upscaler --input "{\"image\": \"$IMG\", \"upscale_factor\": 2}" --json
+```
+
+### Get App Schema
+
+```bash
+# See what inputs an app accepts
+infsh app get falai/flux-dev-lora
+
+# Generate sample input
+infsh app sample falai/flux-dev-lora
+
+# Save sample to file, edit, then run
+infsh app sample falai/flux-dev-lora --save input.json
+# edit input.json...
+infsh app run falai/flux-dev-lora --input input.json --json
+```
+
+### Long-running Tasks
+
+```bash
+# Start without waiting
+infsh app run google/veo-3-1 --input '{"prompt": "..."}' --no-wait
+
+# Check status later
+infsh task get
+
+# Save result when done
+infsh task get --save result.json
+```
+
+## Available Categories
+
+| Category | Apps |
+|----------|------|
+| **Image** | google/nano-banana, google/nano-banana-pro, google/nano-banana-2, falai/flux-dev-lora, bytedance/seedream-5-lite, falai/reve, xai/grok-imagine-image |
+| **Video** | google/veo-*, xai/grok-imagine-video, bytedance/seedance-*, falai/wan-2-5*, infsh/ltx-video, infsh/magi-1 |
+| **Avatar** | bytedance/omnihuman-*, falai/fabric-1-0, falai/pixverse-lipsync |
+| **Upscale** | falai/topaz-image-upscaler, falai/topaz-video-upscaler, infsh/real-esrgan, infsh/thera |
+| **LLMs** | openrouter/claude-*, openrouter/gemini-*, openrouter/kimi-*, openrouter/glm-* |
+| **Search** | tavily/search-assistant, tavily/extract, exa/search, exa/answer, exa/extract |
+| **3D** | infsh/rodin-3d-generator, infsh/hunyuan-image-to-3d-2 |
+| **TTS** | falai/kokoro-tts, falai/dia-tts |
+| **Social** | x/post-tweet, x/post-create, x/dm-send, x/user-follow, x/post-like, x/post-retweet |
+| **Utils** | infsh/agent-browser, infsh/media-merger, infsh/caption-videos, infsh/stitch-images |
+
+## Reference Files
+
+- [Authentication & Setup](references/authentication.md)
+- [Discovering Apps](references/app-discovery.md)
+- [Running Apps](references/running-apps.md)
+- [CLI Reference](references/cli-reference.md)
+
+## Documentation
+
+- [inference.sh Docs](https://inference.sh/docs)
+- [CLI Setup Guide](https://inference.sh/docs/extend/cli-setup)
+- [Apps Overview](https://inference.sh/docs/apps/overview)
diff --git a/skills/inference-sh/cli/references/app-discovery.md b/skills/inference-sh/cli/references/app-discovery.md
new file mode 100644
index 000000000..adcac8c5d
--- /dev/null
+++ b/skills/inference-sh/cli/references/app-discovery.md
@@ -0,0 +1,112 @@
+# Discovering Apps
+
+## List All Apps
+
+```bash
+infsh app list
+```
+
+## Pagination
+
+```bash
+infsh app list --page 2
+```
+
+## Filter by Category
+
+```bash
+infsh app list --category image
+infsh app list --category video
+infsh app list --category audio
+infsh app list --category text
+infsh app list --category other
+```
+
+## Search
+
+```bash
+infsh app search "flux"
+infsh app search "video generation"
+infsh app search "tts" -l
+infsh app search "image" --category image
+```
+
+Or use the flag form:
+
+```bash
+infsh app list --search "flux"
+infsh app list --search "video generation"
+infsh app list --search "tts"
+```
+
+## Featured Apps
+
+```bash
+infsh app list --featured
+```
+
+## Newest First
+
+```bash
+infsh app list --new
+```
+
+## Detailed View
+
+```bash
+infsh app list -l
+```
+
+Shows table with app name, category, description, and featured status.
+
+## Save to File
+
+```bash
+infsh app list --save apps.json
+```
+
+## Your Apps
+
+List apps you've deployed:
+
+```bash
+infsh app my
+infsh app my -l # detailed
+```
+
+## Get App Details
+
+```bash
+infsh app get falai/flux-dev-lora
+infsh app get falai/flux-dev-lora --json
+```
+
+Shows full app info including input/output schema.
+
+## Popular Apps by Category
+
+### Image Generation
+- `falai/flux-dev-lora` - FLUX.2 Dev (high quality)
+- `falai/flux-2-klein-lora` - FLUX.2 Klein (fastest)
+- `infsh/sdxl` - Stable Diffusion XL
+- `google/gemini-3-pro-image-preview` - Gemini 3 Pro
+- `xai/grok-imagine-image` - Grok image generation
+
+### Video Generation
+- `google/veo-3-1-fast` - Veo 3.1 Fast
+- `google/veo-3` - Veo 3
+- `bytedance/seedance-1-5-pro` - Seedance 1.5 Pro
+- `infsh/ltx-video-2` - LTX Video 2 (with audio)
+- `bytedance/omnihuman-1-5` - OmniHuman avatar
+
+### Audio
+- `infsh/dia-tts` - Conversational TTS
+- `infsh/kokoro-tts` - Kokoro TTS
+- `infsh/fast-whisper-large-v3` - Fast transcription
+- `infsh/diffrythm` - Music generation
+
+## Documentation
+
+- [Browsing the Grid](https://inference.sh/docs/apps/browsing-grid) - Visual app browsing
+- [Apps Overview](https://inference.sh/docs/apps/overview) - Understanding apps
+- [Running Apps](https://inference.sh/docs/apps/running) - How to run apps
diff --git a/skills/inference-sh/cli/references/authentication.md b/skills/inference-sh/cli/references/authentication.md
new file mode 100644
index 000000000..3b6519d3d
--- /dev/null
+++ b/skills/inference-sh/cli/references/authentication.md
@@ -0,0 +1,59 @@
+# Authentication & Setup
+
+## Install the CLI
+
+```bash
+curl -fsSL https://cli.inference.sh | sh
+```
+
+## Login
+
+```bash
+infsh login
+```
+
+This opens a browser for authentication. After login, credentials are stored locally.
+
+## Check Authentication
+
+```bash
+infsh me
+```
+
+Shows your user info if authenticated.
+
+## Environment Variable
+
+For CI/CD or scripts, set your API key:
+
+```bash
+export INFSH_API_KEY=your-api-key
+```
+
+The environment variable overrides the config file.
+
+## Update CLI
+
+```bash
+infsh update
+```
+
+Or reinstall:
+
+```bash
+curl -fsSL https://cli.inference.sh | sh
+```
+
+## Troubleshooting
+
+| Error | Solution |
+|-------|----------|
+| "not authenticated" | Run `infsh login` |
+| "command not found" | Reinstall CLI or add to PATH |
+| "API key invalid" | Check `INFSH_API_KEY` or re-login |
+
+## Documentation
+
+- [CLI Setup](https://inference.sh/docs/extend/cli-setup) - Complete CLI installation guide
+- [API Authentication](https://inference.sh/docs/api/authentication) - API key management
+- [Secrets](https://inference.sh/docs/secrets/overview) - Managing credentials
diff --git a/skills/inference-sh/cli/references/cli-reference.md b/skills/inference-sh/cli/references/cli-reference.md
new file mode 100644
index 000000000..50825825f
--- /dev/null
+++ b/skills/inference-sh/cli/references/cli-reference.md
@@ -0,0 +1,104 @@
+# CLI Reference
+
+## Installation
+
+```bash
+curl -fsSL https://cli.inference.sh | sh
+```
+
+## Global Commands
+
+| Command | Description |
+|---------|-------------|
+| `infsh help` | Show help |
+| `infsh version` | Show CLI version |
+| `infsh update` | Update CLI to latest |
+| `infsh login` | Authenticate |
+| `infsh me` | Show current user |
+
+## App Commands
+
+### Discovery
+
+| Command | Description |
+|---------|-------------|
+| `infsh app list` | List available apps |
+| `infsh app list --category ` | Filter by category (image, video, audio, text, other) |
+| `infsh app search ` | Search apps |
+| `infsh app list --search ` | Search apps (flag form) |
+| `infsh app list --featured` | Show featured apps |
+| `infsh app list --new` | Sort by newest |
+| `infsh app list --page ` | Pagination |
+| `infsh app list -l` | Detailed table view |
+| `infsh app list --save ` | Save to JSON file |
+| `infsh app my` | List your deployed apps |
+| `infsh app get ` | Get app details |
+| `infsh app get --json` | Get app details as JSON |
+
+### Execution
+
+| Command | Description |
+|---------|-------------|
+| `infsh app run --input ` | Run app with input file |
+| `infsh app run --input ''` | Run with inline JSON |
+| `infsh app run --input --no-wait` | Run without waiting for completion |
+| `infsh app sample ` | Show sample input |
+| `infsh app sample --save ` | Save sample to file |
+
+## Task Commands
+
+| Command | Description |
+|---------|-------------|
+| `infsh task get ` | Get task status and result |
+| `infsh task get --json` | Get task as JSON |
+| `infsh task get --save ` | Save task result to file |
+
+### Development
+
+| Command | Description |
+|---------|-------------|
+| `infsh app init` | Create new app (interactive) |
+| `infsh app init ` | Create new app with name |
+| `infsh app test --input ` | Test app locally |
+| `infsh app deploy` | Deploy app |
+| `infsh app deploy --dry-run` | Validate without deploying |
+| `infsh app pull ` | Pull app source |
+| `infsh app pull --all` | Pull all your apps |
+
+## Environment Variables
+
+| Variable | Description |
+|----------|-------------|
+| `INFSH_API_KEY` | API key (overrides config) |
+
+## Shell Completions
+
+```bash
+# Bash
+infsh completion bash > /etc/bash_completion.d/infsh
+
+# Zsh
+infsh completion zsh > "${fpath[1]}/_infsh"
+
+# Fish
+infsh completion fish > ~/.config/fish/completions/infsh.fish
+```
+
+## App Name Format
+
+Apps use the format `namespace/app-name`:
+
+- `falai/flux-dev-lora` - fal.ai's FLUX 2 Dev
+- `google/veo-3` - Google's Veo 3
+- `infsh/sdxl` - inference.sh's SDXL
+- `bytedance/seedance-1-5-pro` - ByteDance's Seedance
+- `xai/grok-imagine-image` - xAI's Grok
+
+Version pinning: `namespace/app-name@version`
+
+## Documentation
+
+- [CLI Setup](https://inference.sh/docs/extend/cli-setup) - Complete CLI installation guide
+- [Running Apps](https://inference.sh/docs/apps/running) - How to run apps via CLI
+- [Creating an App](https://inference.sh/docs/extend/creating-app) - Build your own apps
+- [Deploying](https://inference.sh/docs/extend/deploying) - Deploy apps to the cloud
diff --git a/skills/inference-sh/cli/references/running-apps.md b/skills/inference-sh/cli/references/running-apps.md
new file mode 100644
index 000000000..e930d5cfb
--- /dev/null
+++ b/skills/inference-sh/cli/references/running-apps.md
@@ -0,0 +1,171 @@
+# Running Apps
+
+## Basic Run
+
+```bash
+infsh app run user/app-name --input input.json
+```
+
+## Inline JSON
+
+```bash
+infsh app run falai/flux-dev-lora --input '{"prompt": "a sunset over mountains"}'
+```
+
+## Version Pinning
+
+```bash
+infsh app run user/app-name@1.0.0 --input input.json
+```
+
+## Local File Uploads
+
+The CLI automatically uploads local files when you provide a file path instead of a URL. Any field that accepts a URL also accepts a local path:
+
+```bash
+# Upscale a local image
+infsh app run falai/topaz-image-upscaler --input '{"image": "/path/to/photo.jpg", "upscale_factor": 2}'
+
+# Image-to-video from local file
+infsh app run falai/wan-2-5-i2v --input '{"image": "./my-image.png", "prompt": "make it move"}'
+
+# Avatar with local audio and image
+infsh app run bytedance/omnihuman-1-5 --input '{"audio": "/path/to/speech.mp3", "image": "/path/to/face.jpg"}'
+
+# Post tweet with local media
+infsh app run x/post-create --input '{"text": "Check this out!", "media": "./screenshot.png"}'
+```
+
+Supported paths:
+- Absolute paths: `/home/user/images/photo.jpg`
+- Relative paths: `./image.png`, `../data/video.mp4`
+- Home directory: `~/Pictures/photo.jpg`
+
+## Generate Sample Input
+
+Before running, generate a sample input file:
+
+```bash
+infsh app sample falai/flux-dev-lora
+```
+
+Save to file:
+
+```bash
+infsh app sample falai/flux-dev-lora --save input.json
+```
+
+Then edit `input.json` and run:
+
+```bash
+infsh app run falai/flux-dev-lora --input input.json
+```
+
+## Workflow Example
+
+### Image Generation with FLUX
+
+```bash
+# 1. Get app details
+infsh app get falai/flux-dev-lora
+
+# 2. Generate sample input
+infsh app sample falai/flux-dev-lora --save input.json
+
+# 3. Edit input.json
+# {
+# "prompt": "a cat astronaut floating in space",
+# "num_images": 1,
+# "image_size": "landscape_16_9"
+# }
+
+# 4. Run
+infsh app run falai/flux-dev-lora --input input.json
+```
+
+### Video Generation with Veo
+
+```bash
+# 1. Generate sample
+infsh app sample google/veo-3-1-fast --save input.json
+
+# 2. Edit prompt
+# {
+# "prompt": "A drone shot flying over a forest at sunset"
+# }
+
+# 3. Run
+infsh app run google/veo-3-1-fast --input input.json
+```
+
+### Text-to-Speech
+
+```bash
+# Quick inline run
+infsh app run falai/kokoro-tts --input '{"text": "Hello, this is a test."}'
+```
+
+## Task Tracking
+
+When you run an app, the CLI shows the task ID:
+
+```
+Running falai/flux-dev-lora
+Task ID: abc123def456
+```
+
+For long-running tasks, you can check status anytime:
+
+```bash
+# Check task status
+infsh task get abc123def456
+
+# Get result as JSON
+infsh task get abc123def456 --json
+
+# Save result to file
+infsh task get abc123def456 --save result.json
+```
+
+### Run Without Waiting
+
+For very long tasks, run in background:
+
+```bash
+# Submit and return immediately
+infsh app run google/veo-3 --input input.json --no-wait
+
+# Check later
+infsh task get
+```
+
+## Output
+
+The CLI returns the app output directly. For file outputs (images, videos, audio), you'll receive URLs to download.
+
+Example output:
+
+```json
+{
+ "images": [
+ {
+ "url": "https://cloud.inference.sh/...",
+ "content_type": "image/png"
+ }
+ ]
+}
+```
+
+## Error Handling
+
+| Error | Cause | Solution |
+|-------|-------|----------|
+| "invalid input" | Schema mismatch | Check `infsh app get` for required fields |
+| "app not found" | Wrong app name | Check `infsh app list --search` |
+| "quota exceeded" | Out of credits | Check account balance |
+
+## Documentation
+
+- [Running Apps](https://inference.sh/docs/apps/running) - Complete running apps guide
+- [Streaming Results](https://inference.sh/docs/api/sdk/streaming) - Real-time progress updates
+- [Setup Parameters](https://inference.sh/docs/apps/setup-parameters) - Configuring app inputs
diff --git a/tests/tools/test_infsh_tool.py b/tests/tools/test_infsh_tool.py
new file mode 100644
index 000000000..f866df1e3
--- /dev/null
+++ b/tests/tools/test_infsh_tool.py
@@ -0,0 +1,114 @@
+"""Tests for tools/infsh_tool.py — inference.sh CLI integration."""
+
+import json
+import subprocess
+from unittest.mock import patch, MagicMock
+
+import pytest
+
+from tools.infsh_tool import (
+ check_infsh_requirements,
+ infsh_tool,
+ infsh_install,
+)
+
+
+class TestCheckRequirements:
+ def test_returns_bool(self):
+ result = check_infsh_requirements()
+ assert isinstance(result, bool)
+
+ def test_returns_true_when_infsh_on_path(self, monkeypatch):
+ monkeypatch.setattr("shutil.which", lambda cmd: "/usr/local/bin/infsh" if cmd == "infsh" else None)
+ assert check_infsh_requirements() is True
+
+ def test_returns_false_when_missing(self, monkeypatch):
+ monkeypatch.setattr("shutil.which", lambda cmd: None)
+ assert check_infsh_requirements() is False
+
+
+class TestInfshTool:
+ def test_not_installed_returns_error(self, monkeypatch):
+ monkeypatch.setattr("tools.infsh_tool.check_infsh_requirements", lambda: False)
+ result = json.loads(infsh_tool("app list"))
+ assert result["success"] is False
+ assert "not installed" in result["error"].lower()
+
+ def test_successful_command(self, monkeypatch):
+ monkeypatch.setattr("tools.infsh_tool.check_infsh_requirements", lambda: True)
+ mock_result = MagicMock()
+ mock_result.returncode = 0
+ mock_result.stdout = '{"apps": ["flux", "veo"]}'
+ mock_result.stderr = ""
+
+ with patch("subprocess.run", return_value=mock_result) as mock_run:
+ result = json.loads(infsh_tool("app list --search flux"))
+ assert result["success"] is True
+ mock_run.assert_called_once()
+ call_cmd = mock_run.call_args[0][0]
+ assert "infsh app list --search flux" in call_cmd
+
+ def test_failed_command(self, monkeypatch):
+ monkeypatch.setattr("tools.infsh_tool.check_infsh_requirements", lambda: True)
+ mock_result = MagicMock()
+ mock_result.returncode = 1
+ mock_result.stdout = ""
+ mock_result.stderr = "unknown command"
+
+ with patch("subprocess.run", return_value=mock_result):
+ result = json.loads(infsh_tool("badcommand"))
+ assert result["success"] is False
+ assert result["exit_code"] == 1
+
+ def test_timeout_handled(self, monkeypatch):
+ monkeypatch.setattr("tools.infsh_tool.check_infsh_requirements", lambda: True)
+
+ with patch("subprocess.run", side_effect=subprocess.TimeoutExpired("infsh", 300)):
+ result = json.loads(infsh_tool("app run something", timeout=300))
+ assert result["success"] is False
+ assert "timed out" in result["error"].lower()
+
+ def test_json_output_parsed(self, monkeypatch):
+ monkeypatch.setattr("tools.infsh_tool.check_infsh_requirements", lambda: True)
+ mock_result = MagicMock()
+ mock_result.returncode = 0
+ mock_result.stdout = '{"url": "https://example.com/image.png"}'
+ mock_result.stderr = ""
+
+ with patch("subprocess.run", return_value=mock_result):
+ result = json.loads(infsh_tool("app run flux --json"))
+ assert result["success"] is True
+ assert isinstance(result["output"], dict)
+ assert result["output"]["url"] == "https://example.com/image.png"
+
+
+class TestInfshInstall:
+ def test_already_installed(self, monkeypatch):
+ monkeypatch.setattr("tools.infsh_tool.check_infsh_requirements", lambda: True)
+ monkeypatch.setattr("tools.infsh_tool._get_infsh_path", lambda: "/usr/local/bin/infsh")
+ mock_result = MagicMock()
+ mock_result.returncode = 0
+ mock_result.stdout = "infsh v1.2.3"
+
+ with patch("subprocess.run", return_value=mock_result):
+ result = json.loads(infsh_install())
+ assert result["success"] is True
+ assert result["already_installed"] is True
+
+
+class TestToolRegistration:
+ def test_tools_registered(self):
+ from tools.registry import registry
+ assert "infsh" in registry._tools
+ assert "infsh_install" in registry._tools
+
+ def test_infsh_in_inference_toolset(self):
+ from toolsets import TOOLSETS
+ assert "inference" in TOOLSETS
+ assert "infsh" in TOOLSETS["inference"]["tools"]
+ assert "infsh_install" in TOOLSETS["inference"]["tools"]
+
+ def test_infsh_not_in_core_tools(self):
+ from toolsets import _HERMES_CORE_TOOLS
+ assert "infsh" not in _HERMES_CORE_TOOLS
+ assert "infsh_install" not in _HERMES_CORE_TOOLS
diff --git a/tools/infsh_tool.py b/tools/infsh_tool.py
new file mode 100644
index 000000000..ede785dd1
--- /dev/null
+++ b/tools/infsh_tool.py
@@ -0,0 +1,302 @@
+#!/usr/bin/env python3
+"""
+Inference.sh Tool Module
+
+A simple tool for running AI apps via the inference.sh CLI (infsh).
+Provides two functions:
+ - infsh_install: Install the infsh CLI
+ - infsh: Run any infsh command
+
+This is a lightweight wrapper that gives agents direct access to 150+ AI apps
+including image generation (FLUX, Reve), video (Veo, Wan), LLMs, search, and more.
+
+Usage:
+ from tools.infsh_tool import infsh_tool, infsh_install
+
+ # Install the CLI
+ result = infsh_install()
+
+ # Search for apps first (always do this!)
+ result = infsh_tool("app list --search flux")
+
+ # Run an app
+ result = infsh_tool("app run falai/flux-dev-lora --input '{\"prompt\": \"a cat\"}' --json")
+"""
+
+import json
+import logging
+import os
+import shutil
+import subprocess
+from typing import Optional
+
+logger = logging.getLogger(__name__)
+
+
+# ---------------------------------------------------------------------------
+# Configuration
+# ---------------------------------------------------------------------------
+
+DEFAULT_TIMEOUT = 300 # 5 minutes for long-running AI tasks
+INSTALL_TIMEOUT = 60
+
+
+# ---------------------------------------------------------------------------
+# Availability check
+# ---------------------------------------------------------------------------
+
+def check_infsh_requirements() -> bool:
+ """Check if infsh is available in PATH."""
+ return shutil.which("infsh") is not None
+
+
+def _get_infsh_path() -> Optional[str]:
+ """Get the path to infsh binary."""
+ return shutil.which("infsh")
+
+
+# ---------------------------------------------------------------------------
+# Install function
+# ---------------------------------------------------------------------------
+
+def infsh_install() -> str:
+ """
+ Install the inference.sh CLI.
+
+ Downloads and installs the infsh binary using the official installer script.
+ The installer detects OS/arch, downloads the correct binary, verifies checksum,
+ and places it in PATH.
+
+ Returns:
+ JSON string with success/error status
+ """
+ try:
+ # Check if already installed
+ if check_infsh_requirements():
+ infsh_path = _get_infsh_path()
+ # Get version
+ version_result = subprocess.run(
+ ["infsh", "--version"],
+ capture_output=True,
+ text=True,
+ timeout=10
+ )
+ version = version_result.stdout.strip() if version_result.returncode == 0 else "unknown"
+ return json.dumps({
+ "success": True,
+ "message": f"infsh is already installed at {infsh_path}",
+ "version": version,
+ "already_installed": True
+ })
+
+ # Run the installer
+ result = subprocess.run(
+ ["sh", "-c", "curl -fsSL https://cli.inference.sh | sh"],
+ capture_output=True,
+ text=True,
+ timeout=INSTALL_TIMEOUT,
+ env={**os.environ, "NONINTERACTIVE": "1"}
+ )
+
+ if result.returncode != 0:
+ return json.dumps({
+ "success": False,
+ "error": f"Installation failed: {result.stderr}",
+ "stdout": result.stdout
+ })
+
+ # Verify installation
+ if not check_infsh_requirements():
+ return json.dumps({
+ "success": False,
+ "error": "Installation completed but infsh not found in PATH. You may need to restart your shell or add ~/.local/bin to PATH.",
+ "stdout": result.stdout
+ })
+
+ return json.dumps({
+ "success": True,
+ "message": "infsh installed successfully",
+ "stdout": result.stdout,
+ "next_step": "Run 'infsh login' to authenticate, or set INFSH_API_KEY environment variable"
+ })
+
+ except subprocess.TimeoutExpired:
+ return json.dumps({
+ "success": False,
+ "error": f"Installation timed out after {INSTALL_TIMEOUT}s"
+ })
+ except Exception as e:
+ logger.exception("infsh_install error: %s", e)
+ return json.dumps({
+ "success": False,
+ "error": f"Installation error: {type(e).__name__}: {e}"
+ })
+
+
+# ---------------------------------------------------------------------------
+# Main tool function
+# ---------------------------------------------------------------------------
+
+def infsh_tool(
+ command: str,
+ timeout: Optional[int] = None,
+) -> str:
+ """
+ Execute an infsh CLI command.
+
+ Args:
+ command: The infsh command to run (without the 'infsh' prefix).
+ Examples: "app list", "app run falai/flux-schnell --input '{}'"
+ timeout: Command timeout in seconds (default: 300)
+
+ Returns:
+ JSON string with output, exit_code, and error fields
+ """
+ try:
+ effective_timeout = timeout or DEFAULT_TIMEOUT
+
+ # Check if infsh is installed
+ if not check_infsh_requirements():
+ return json.dumps({
+ "success": False,
+ "error": "infsh CLI is not installed. Use infsh_install to install it first.",
+ "hint": "Call the infsh_install tool to set up the CLI"
+ })
+
+ # Build the full command
+ full_command = f"infsh {command}"
+
+ # Execute
+ result = subprocess.run(
+ full_command,
+ shell=True,
+ capture_output=True,
+ text=True,
+ timeout=effective_timeout,
+ env=os.environ
+ )
+
+ output = result.stdout
+ error = result.stderr
+
+ # Try to parse JSON output if present
+ parsed_output = None
+ if output.strip():
+ try:
+ parsed_output = json.loads(output)
+ except json.JSONDecodeError:
+ pass # Not JSON, keep as string
+
+ response = {
+ "success": result.returncode == 0,
+ "exit_code": result.returncode,
+ "output": parsed_output if parsed_output is not None else output,
+ }
+
+ if error:
+ response["stderr"] = error
+
+ return json.dumps(response, indent=2)
+
+ except subprocess.TimeoutExpired:
+ return json.dumps({
+ "success": False,
+ "error": f"Command timed out after {effective_timeout}s",
+ "hint": "For long-running tasks, consider using --no-wait flag"
+ })
+ except Exception as e:
+ logger.exception("infsh_tool error: %s", e)
+ return json.dumps({
+ "success": False,
+ "error": f"Execution error: {type(e).__name__}: {e}"
+ })
+
+
+# ---------------------------------------------------------------------------
+# Registry
+# ---------------------------------------------------------------------------
+
+from tools.registry import registry
+
+INFSH_TOOL_DESCRIPTION = """Run AI apps via inference.sh CLI. Access 150+ apps for image generation, video, LLMs, search, 3D, and more.
+
+One API key for everything - manage all AI services (FLUX, Veo, Claude, Tavily, etc.) with a single inference.sh account. You can also bring your own API keys.
+
+IMPORTANT: Always use 'app list --search ' first to find the exact app ID before running. App names change frequently.
+
+Commands:
+- app list --search : Find apps (ALWAYS DO THIS FIRST)
+- app run --input '' --json: Run an app
+- app get : Get app schema before running
+
+Verified app examples (use --search to confirm current names):
+- Image: google/nano-banana, google/nano-banana-pro, google/nano-banana-2, falai/flux-dev-lora, bytedance/seedream-5-lite, falai/reve, xai/grok-imagine-image
+- Video: google/veo-3-1-fast, bytedance/seedance-1-5-pro, falai/wan-2-5
+- Upscale: falai/topaz-image-upscaler
+- Search: tavily/search-assistant, exa/search
+- LLM: openrouter/claude-sonnet-45
+
+Workflow: ALWAYS search first, then run:
+1. app list --search image
+2. app run falai/flux-dev-lora --input '{"prompt": "a sunset"}' --json"""
+
+INFSH_SCHEMA = {
+ "name": "infsh",
+ "description": INFSH_TOOL_DESCRIPTION,
+ "parameters": {
+ "type": "object",
+ "properties": {
+ "command": {
+ "type": "string",
+ "description": "The infsh command (without 'infsh' prefix). ALWAYS use 'app list --search ' first to find correct app IDs, then 'app run --input --json'"
+ },
+ "timeout": {
+ "type": "integer",
+ "description": "Max seconds to wait (default: 300). AI tasks like video generation may take 1-2 minutes.",
+ "minimum": 1
+ }
+ },
+ "required": ["command"]
+ }
+}
+
+INFSH_INSTALL_SCHEMA = {
+ "name": "infsh_install",
+ "description": "Install the inference.sh CLI (infsh). Downloads and installs the binary. Run this first if infsh is not available.",
+ "parameters": {
+ "type": "object",
+ "properties": {},
+ "required": []
+ }
+}
+
+
+def _handle_infsh(args, **kw):
+ return infsh_tool(
+ command=args.get("command", ""),
+ timeout=args.get("timeout"),
+ )
+
+
+def _handle_infsh_install(args, **kw):
+ return infsh_install()
+
+
+# Register both tools under the "inference" toolset
+registry.register(
+ name="infsh",
+ toolset="inference",
+ schema=INFSH_SCHEMA,
+ handler=_handle_infsh,
+ check_fn=check_infsh_requirements,
+ requires_env=[],
+)
+
+registry.register(
+ name="infsh_install",
+ toolset="inference",
+ schema=INFSH_INSTALL_SCHEMA,
+ handler=_handle_infsh_install,
+ check_fn=lambda: True, # Always available - it's the installer
+ requires_env=[],
+)
diff --git a/toolsets.py b/toolsets.py
index 1a73ff1b8..b94ea580d 100644
--- a/toolsets.py
+++ b/toolsets.py
@@ -183,6 +183,12 @@ TOOLSETS = {
"tools": ["execute_code"],
"includes": []
},
+
+ "inference": {
+ "description": "inference.sh CLI (infsh) — run 150+ AI apps: image gen (FLUX, Reve), video (Veo, Wan), LLMs, search (Tavily, Exa), 3D, and more",
+ "tools": ["infsh", "infsh_install"],
+ "includes": []
+ },
"delegation": {
"description": "Spawn subagents with isolated context for complex subtasks",