Skip to content

Repository files navigation

runai

runai

One terminal-native router for your AI CLI skills

跨 Claude Code / Codex / Gemini CLI / OpenCode 的统一 skill / MCP 管理 + LLM 智能路由器 + 实时遥测仪表盘。

English  |  中文

Quickstart  ·  Three Pillars  ·  Architecture  ·  AGENT guide

Single Rust binary · macOS / Linux / Windows · No runtime deps · CC BY-NC-SA 4.0


Architecture

runai architecture

One-liner

runai unifies how you install, enable, recommend, and observe AI CLI skills across four host CLIs. Skills are real folders on disk symlinked into each CLI's skills directory; MCP servers are real entries in each CLI's config file. Filesystem = source of truth, DB only holds metadata.

On top of that core, an opt-in LLM skill router auto-picks the right skill for every user prompt (BM25 prefilter + LLM rerank with verified-adoption counting), and a local dashboard at http://127.0.0.1:17888 shows every hook invocation, token cost, latency, and chosen skill in real time.


Pain points it solves

Before runai
Skills scattered across Claude Code / Codex / Gemini / OpenCode, each with its own config quirks One TUI + CLI + MCP server manages all four; native config format per target
git clone a skill repo, copy folders, edit JSON / TOML by hand, repeat for every CLI runai install owner/repo — downloads, registers, groups, symlinks into every CLI in one shot
2000+ skills out there, no way to browse without leaving the terminal Built-in market: runai market browses cached index, Enter to install
Hard-deleted skills can't be recovered when you change your mind Trash-first: runai uninstall moves to ~/.runai/trash/, runai trash restore brings it back
"Did I enable that skill?" — ls four directories, compare with config files, hope they agree Source of truth = symlink existence + config entry presence; runai status reads filesystem live
No idea which skills you actually use, no idea what the router is doing per turn Dashboard at 127.0.0.1:17888 — every routing attempt logs Fast/Precise mode, candidates, optional Precise expansion, router input/output, hook output, latency, tokens, recovery and empty attribution

Three pillars

1. Multi-CLI skill / MCP manager

  • Install once, enabled everywhererunai install owner/repo[@branch] downloads the skill, registers it in DB, and symlinks into all four CLI skill dirs. MCP entries get written into each CLI's native config (Claude JSON / Codex TOML / Gemini JSON / OpenCode JSON).
  • Filesystem = truth — Skill enabled ⇔ symlink exists at <cli-home>/skills/<name>. MCP enabled ⇔ entry present (without "disabled": true) in target config. The DB is metadata-only; nothing breaks if you blow it away.
  • Groups — Cluster related skills (figma, ktv-car-project, ppt-slides, …) into named groups; enable / disable / rename whole groups atomically.
  • Market — Built-in skill marketplace with 2,000+ skills curated; cached locally, refreshed in the background (1h TTL). runai market install <name> is one-shot.
  • Safe delete — Everything trash-first. Restore until you runai trash purge.

2. LLM skill router (opt-in)

  • Hook integration — Claude Code's UserPromptSubmit hook → runai recommend → router decides → output injected into the agent's prompt as additional context. In dashboard/team use, local hooks read ~/.runai-identity; a valid key applies that user's dashboard preferences, while a stale key returns empty hook output instead of silently reverting to anonymous defaults.
  • Fast by default, Precise on demand — Fast builds a bounded head+tail task anchor, retrieves from structured task / triggers / inputs / outputs fields, and makes at most one router-model call. It never injects transcript history, project files, conversation replay, or session intent memory. Precise adds one bounded semantic expansion call and may use opted-in context; cwd, client kind, transcript messages and Conversation replay all have hard per-field/turn/total caps.
  • Evidence-bounded retrieval — BM25 and bilingual structured triggers rank a configurable candidate set. Quality/adoption/feedback can re-rank candidates that have retrieval evidence, but cannot revive zero-evidence skills into an oversized Fast pool. A fully zero-evidence oversized pool returns empty with retrieval_zero; a tiny pool may still reach the router because BM25 IDF can be zero in small corpora.
  • AI summary enrichment — Every skill gets a structured summary (task / triggers / inputs / outputs / not-for / score) generated by the same LLM in your chosen summary_lang; positive fields feed the BM25 index while not-for stays as a negative/admission signal and router context. Enrichment is gated on an explicit language pick and the output language is enforced (mismatches retry, then drop rather than write), so the index stays single-language; triggers keeps cross-language keywords for recall. Auto-refreshes on SKILL.md edit, and runai install / scan fire targeted re-enrich for just the changed skills.
  • Strict short-ID routing — Each request maps candidates to C01..Cnn; parsed output is filtered through that request's whitelist. not-for is a veto. Alternatives use EXCLUSIVE; necessary complementary workflow steps use COMPATIBLE. The same relevant skill may be recommended again on a later turn; there is no session no-repeat suppression.
  • Verified adoption — Hook output tells the agent to run runai-client activate <skill>, optionally with a literal runai-owned rnai_sess_* session id. That command prints SKILL.md only after the server has ACKed /skills/use/{name} or after a durable local outbox event has been written under ~/.runai/client-cache/servers/<server-key>/skills/<skill-key>/.outbox/.
  • Client-side cacherunai-client activate and sync cache the whole skill directory under ~/.runai/client-cache, never under the managed ~/.runai/skills pool. Cache hits still send or queue a usage event before printing SKILL.md, so reduced content traffic does not drop adoption counts. When a skill references support files inside its bundle, agents read them with runai-client file <skill> <relpath> from that cache; runtime/user-home paths such as ~/.tool-name/... remain local filesystem data, not bundle files.

3. Realtime telemetry dashboard

  • Single binary, no CDNrunai server boots an embedded axum HTTP server; web/{index.html,app.css,app.js} are include_str!'d into the Rust binary.
  • Auto-launch on every Claude Code sessionrunai server --install-hook adds a SessionStart hook so the dashboard is always at http://127.0.0.1:17888 when you open Claude Code.
  • Every routing attempt instrumented — Per-event telemetry includes Fast/Precise mode, actual LLM attempt count, empty attribution, retrieval query, parsed/filtered candidates, parser recovery, candidate set, raw/cleaned Precise expansion, router input/raw output, hook output, latency and tokens.
  • Admin provider checks — Dashboard Admin separates the global recommend switch from per-user preferences and can test a saved provider by sending a tiny real model request, surfacing success or the provider error inline. Provider/model changes are global config; prompt-injection toggles, recommend context controls (session memory on/off + queue limit + BM25 candidate count), reminder text, and library scope are per-user preferences and only affect authenticated /recommend calls or local hooks with a valid identity.
  • Per-skill drill-down/skills lists every managed skill with usage count, LLM quality score, AI summary; click into one to see its full directory tree (browse SKILL.md + supporting files), recent usage events, raw description vs. enriched summary.
  • Live polling — 5s refresh with inFlight guard and visibilitychange pause. Per-boot cache-buster on static assets means a server restart after cargo install propagates without a hard refresh.

Quickstart

Install

One-liner (macOS / Linux) — detects your platform, downloads + checksum-verifies the release binary, puts it on PATH, then sets up the edition you pick:

curl -fsSL https://raw.githubusercontent.com/Crosery/runai/main/install.sh | sh
# non-interactive (agents / CI):
curl -fsSL https://raw.githubusercontent.com/Crosery/runai/main/install.sh | sh -s -- --edition personal --yes

Windows (PowerShell):

irm https://raw.githubusercontent.com/Crosery/runai/main/install.ps1 | iex

From source (needs Rust):

cargo install --git https://github.com/Crosery/runai

Editions (one binary, picked at install time): personal = local skill router + Claude Code hook, no account, no server. team = run the multi-user dashboard server; other machines connect with curl -fsSL http://<host>:17888/install | bash.

Installer flags: --edition personal|team, --version <tag>, --bin-dir <path>, --yes, --no-hook, --no-setup, --dry-run, --uninstall. Prebuilt artifacts for {linux,darwin,windows} × {amd64,arm64} on the releases page. Windows needs Developer Mode or Administrator for symlinks.

First-run setup

# 1) Boot the TUI to browse / enable existing skills you already have plus 2000+ market entries
runai

# 2) Opt-in to the LLM router (default DeepSeek v4-flash, ~$0.0001 per route call)
runai recommend setup
runai recommend install-hook          # writes UserPromptSubmit + SessionStart hooks
                                       # into ~/.claude/settings.json (idempotent, .runai-bak backup)

# 3) Launch the dashboard once; the hook keeps it running thereafter
runai server --port 17888 --ensure
runai server --install-hook            # auto-launch on every Claude Code session

After step 2, every Claude Code prompt routes through runai recommend; accepted skills are activated through runai-client activate, which records or queues adoption before printing cached content, and every router event lands in the dashboard.

Daily commands

runai                                 # TUI
runai install owner/repo              # install skill from GitHub into all CLIs
runai market install <name>           # install from market
runai search <query>                  # search installed + market
runai status                          # show enabled / disabled across all CLIs
runai list --target claude            # one-CLI view
runai backup                          # timestamped backup of skills + configs
runai trash                           # browse deleted, restore or purge
runai recommend enrich                # regenerate AI summaries (changed-mtime detection)
runai recommend enrich --fix-lang     # re-enrich only summaries in the wrong language
runai recommend stats                 # router LLM usage / cost / latency over time
runai doctor                          # health check; `--fix` prunes dangling symlinks

Full CLI list: runai --help.


What lives where

~/.runai/                              ~/.{claude,codex,gemini,opencode}/skills/
├── skills/<name>/SKILL.md            └── <name> -> ~/.runai/skills/<name>     ← symlink = enabled
├── mcps/<name>.json                  ~/.claude.json          ← MCP entries (Claude)
├── groups/<id>.toml                  ~/.codex/config.toml    ← MCP entries (Codex)
├── trash/<trash-id>/                 ~/.gemini/settings.json ← MCP entries (Gemini)
├── backups/<timestamp>/              ~/.config/opencode/opencode.json ← MCP entries (OpenCode)
├── market-cache/
├── users/<user_id>/skills/<name>/   ← v0.11.0-beta.5: per-user private skill isolation
├── config.toml                        ← runai recommend config (provider, model, api_key)
└── runai.db                           ← SQLite: skill metadata, usage, router_events, AI summaries

Auto-migrated from ~/.skill-manager/ on first launch (v0.5.0 transition). Env overrides honored: RUNE_DATA_DIR and SKILL_MANAGER_DATA_DIR.

skills.sh aggregator (v0.11.0-beta.5)

The dashboard's Market tab is a mirror of the skills.sh leaderboard — 20K+ skills across 2.6K GitHub repos, no API key needed.

  • All Time / Trending (24h) / Hot sort tabs match skills.sh, all sorted server-side
  • 8W TREND column: per-row sparkline (from skills.sh weeklyInstalls)
  • INSTALLS column: formatted as 1.8M / 478.6K
  • Server-side pagination: 50/page default + prev / next buttons — no 20K-row freeze
  • Search filters via server-side query (250ms debounce)
  • Click install: runai resolves the real GitHub repo on demand and drops the whole skill directory into ~/.runai/users/<user_id>/skills/<name>/
  • Login persistence: api_key stored in localStorage so a fresh browser session stays signed in
  • The old per-repo builtin sources were retired; add your own GitHub repos via + GitHub (stored as user-added sources)

Auto-start at login (v0.11.0-beta.5)

runai server --install-autostart       # register login auto-start
runai server --uninstall-autostart     # remove it
  • macOS: writes ~/Library/LaunchAgents/cn.crosery.runai.plist and launchctl load -w. KeepAlive relaunches the server if it crashes.
  • Linux: writes ~/.config/systemd/user/runai.service and systemctl --user enable --now. Restart=on-failure.
  • Windows: not implemented yet — the command prints Task Scheduler instructions.

runai (the default TUI) also auto-spawns the server via ensure_running. Set RUNAI_NO_AUTOSPAWN=1 to disable.

Multi-user private skills (v0.11.0-beta.5)

runai server now isolates skills per registered user. Every dashboard / hook user installs into their own ~/.runai/users/<user_id>/skills/<name>/; the DB column owner_user_id separates public from private rows.

  • Public skills (owner_user_id IS NULL) live in ~/.runai/skills/<name>/ and are visible to every user
  • Private skills are visible only to their owner; admin sees everything via scope "*"
  • The same skill name can coexist as public + N user-private copies; DB ids gain a u:<uid>: prefix to avoid PK collisions
  • After running the client install script (curl <SERVER>/install | bash) and registering an account, dashboard installs go private. CLI / TUI installs still write to the public pool.

See the "Per-user physical skill isolation" section in AGENTS.md for the full invariant set.

Admin password reset

When a user forgets their password, an admin has three supported ways to reset it. All three rotate the target's api_key too, so every previously-issued Bearer (browser cookie / installed client) dies and the user must log in again with the new password.

Local CLI (on the machine running the server; writes the local runai.db directly, no server needs to be up):

runai admin reset-password <username>                 # interactive hidden prompt + confirm
runai admin reset-password <username> --password <pw> # non-interactive, for scripts / agents

This is the supported replacement for hand-editing the users table via SQL. An unknown username errors cleanly (no panic).

Server API (team mode, admin Bearer / session cookie):

curl -X POST http://<host>:17888/api/admin/users/<user_id>/reset-password \
  -H "Authorization: Bearer <admin_key>" \
  -H "Content-Type: application/json" \
  -d '{"new_password":"<new password>"}'

200 on success (the response carries only user_id / username — the new key is never returned); 403 for non-admin, 401 unauthenticated, 404 unknown user, 400 when the password is shorter than 6 chars. An admin may reset their own password.

Dashboard (team mode, logged in as admin): Admin tab → user table → 重置密码 button on each row (works on your own row too). Prompts for the new password, calls the same /api/admin/users/{id}/reset-password endpoint, then confirms with a reminder to hand the new password to the user — their old api_key/session is already dead.


Project layout

Module Source What it does
cli/ src/cli/mod.rs clap subcommand dispatch; entry point for every runai <verb>
core::manager src/core/manager.rs SkillManager orchestrates install / enable / disable / trash / migrate
core::scanner src/core/scanner.rs Filesystem discovery + adoption of unmanaged skills (with cross-data-dir safety guard)
core::linker src/core/linker.rs Cross-platform symlink create / remove / detect
core::recommend src/core/recommend.rs LLM skill router (BM25 + AI summary + LLM rerank + adoption tracking)
core::db src/core/db.rs SQLite schema (v14) + migrations + queries
core::installer src/core/installer.rs GitHub / market install pipeline
mcp::tools src/mcp/tools.rs 22 sm_* tools exposed via MCP stdio
tui/ src/tui/ ratatui + crossterm full-screen UI
server src/server.rs axum dashboard for router telemetry

Per-module deep-dive docs in src/**/*.LLM.md. Architecture invariants in AGENTS.md.


Design principles

  • Filesystem is the source of truth. Skill enabled = symlink exists. MCP enabled = config entry present. DB carries metadata only; rebuild it from disk any time.
  • Trash-first everywhere. Delete is reversible until runai trash purge. Backups timestamped, restorable.
  • Single binary, no runtime deps. Web dashboard assets include_str!'d in. rusqlite bundled. No node, no python, no Docker.
  • Router is opt-in. Default enabled = false; nothing reaches a network until runai recommend setup.
  • Verified adoption over self-report. Counting comes from runai-client activate: cached content is printed only after /skills/use/{name} is ACKed or a durable local outbox event is written.
  • Safety guards on destructive syscalls. scan / adopt refuse to rename across data dirs after the 2026-04-27 incident. Physical-e2e tests in tests/safety_e2e.rs lock the invariant.
  • Documentation invariant. Every code change ships its *.LLM.md update in the same commit (see AGENTS.md).

License

CC BY-NC-SA 4.0 — Attribution-NonCommercial-ShareAlike 4.0 International.

  • Non-commercial only — you may not use the material for commercial purposes.
  • ShareAlike — if you remix, transform, or build upon the material, you must distribute your contributions under the same license.
  • Attribution — credit the original author and link back to this repository.

Full text: creativecommons.org/licenses/by-nc-sa/4.0.

About

AI CLI skill/MCP resource manager with TUI and MCP server — manage skills, MCPs, groups across Claude/Codex/Gemini/OpenCode

Resources

Stars

12 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages