Yoki Plugin SDK

5分でプラグインを作成

すべてJavaScriptで動作 — SDKはYokiに内蔵、npm installは不要。このガイドで最初のプラグインを一から作成できます。

ステップ1:フォルダを作成

各プラグインは ~/yoki/plugins/ 内の専用フォルダにあります:

mkdir ~/yoki/plugins/my-plugin

またはCLIで自動生成:

npx create-yoki-pluginplugin.json + main.js をすぐに生成

ステップ2:plugin.jsonを追加

このファイルはプラグインの情報をYokiに伝えます — 名前、キーワード、実行するスクリプト。

~/yoki/plugins/my-plugin/plugin.json
{
  "name": "My Plugin",
  "keyword": "my",
  "icon": "star",
  "version": "1.0.0",
  "protocol": "v2",
  "commands": [
    {
      "name": "main",
      "title": "My Plugin",
      "mode": "detail",
      "exec": "main.js",
      "takes_query": true
    }
  ]
}
keywordユーザーがプラグインを起動するために入力するもの
mode結果の表示方法。detail = カード、list = リスト
exec実行するスクリプトファイル
takes_querytrue = キーワードの後にテキストを入力可能

ステップ3:スクリプトを書く

スクリプトはYokiから入力を読み取り、処理してレスポンスを返します。

~/yoki/plugins/my-plugin/main.js
const { readInput, writeResponse, detail } = require("@yoki/plugin-sdk")

async function main() {
  const input = await readInput()
  const query = input.query || ""

  if (!query) {
    writeResponse(detail('<div style="padding:16px;color:#888">Type something after <b>my</b></div>'))
    return
  }

  writeResponse(detail(
    `<div style="padding:16px">
      <div style="font-size:24px;font-weight:bold;color:#4FC3F7">${query.toUpperCase()}</div>
      <div style="margin-top:8px;color:#888">Your input, uppercased</div>
    </div>`,
    [{ label: "Original", value: query }, { label: "Result", value: query.toUpperCase() }],
    [{ title: "Copy result", type: "copy", value: query.toUpperCase() }]
  ))
}
main()

ステップ4:テスト

Yokiなしでテスト — JSONをスクリプトにパイプ:

echo '{"query":"hello world","command":"main","context":{}}' | node main.js

次のように表示されます:

{"type":"detail","markdown":"<div style=\"padding:16px\"><div style=\"font-size:24px;font-weight:bold;color:#4FC3F7\">HELLO WORLD</div><div style=\"margin-top:8px;color:#888\">Your input, uppercased</div></div>","metadata":[{"label":"Original","value":"hello world"},{"label":"Result","value":"HELLO WORLD"}],"actions":[{"title":"Copy result","type":"copy","value":"HELLO WORLD"}]}

Yokiを開き、my hello world と入力 — 「HELLO WORLD」のカードが表示されます。Enterでコピー。

ステップ5:次のステップ

modeを "list" に変更 — スクロール可能な複数アイテムを返す
actionsを追加 — コピー、URL開く、コマンド実行のボタン
metadataを追加 — サイドバーのキー値ペア
background modeを使用 — UI表示なしのアクション用
マーケットプレイスに公開 — GitHubにプッシュ、Yokiに送信

SDK全関数のリファレンスを読み進めてください。

SDKリファレンス

Yoki SDKの全関数と実例。インストール不要。

インポート
const { readInput, writeResponse, detail, list, background, error, stripKeyword, escHtml } = require("@yoki/plugin-sdk")
readInput() Reads what the user typed and context from Yoki.
writeResponse(resp) Sends your response back to Yoki.
detail(html, metadata?, actions?) Shows a rich card with HTML content, a metadata sidebar, and action buttons.
list(items) Shows a scrollable list of items.
background(hud, notification?) Runs an action without showing any UI.
error(message, details?) Shows an error message to the user.
stripKeyword(query, ...keywords) Removes a keyword prefix from the query string.
escHtml(str) Escapes HTML special characters (&, <, >, ").

readInput()

Reads what the user typed and context from Yoki. Call this once at the start of your script. Returns an object with the user's query, which command was triggered, and context (paths, locale, credentials).

シグネチャ
async readInput(): Promise<object>
Get the user query
const input = await readInput()
const query = input.query  // "hello world"
Access the data directory (for saving files)
const input = await readInput()
const dataDir = input.context.data_dir
// e.g. "C:/Users/you/yoki/plugins/my-plugin/data"
// Use this to store config, cache, tokens — it persists across runs
Read credentials (for API plugins)
const input = await readInput()
const apiKey = input.context.credentials?.api_key || ""
if (!apiKey) {
  writeResponse(error("API key not configured"))
  return
}
Full input structure
// input = {
//   query: "hello world",         — what the user typed after keyword
//   command: "main",              — which command (from plugin.json)
//   action: "search",             — "search" | "execute" | "refresh"
//   context: {
//     yoki_version: "1.0.8.2",
//     os: "windows",
//     locale: "en",               — user's language
//     plugin_dir: "C:/.../my-plugin",
//     data_dir: "C:/.../my-plugin/data",
//     credentials: { ... }        — from credentials service
//   },
//   preferences: {}
// }

writeResponse(resp)

Sends your response back to Yoki. Call this once at the end of your script. Pass the result from any builder function — detail(), list(), error(), etc.

シグネチャ
writeResponse(resp: object): void
Basic usage
writeResponse(detail("<div>Hello!</div>"))
Send an error
writeResponse(error("Something went wrong", "Please try again later"))

detail(html, metadata?, actions?)

Shows a rich card with HTML content, a metadata sidebar, and action buttons. When the user presses Enter, the first action with type "copy" is triggered.

シグネチャ
detail(
  html: string,
  metadata?: [{ label: string, value: string }, ...],
  actions?:  [{ title: string, type: string, value?: string }, ...]
)
Simple card
writeResponse(detail(
  '<div style="padding:16px;font-size:20px">Hello, World!</div>'
))
Card with metadata sidebar
writeResponse(detail(
  '<div style="padding:16px"><h2>2 + 2 = 4</h2></div>',
  [
    { label: "Hex", value: "0x4" },
    { label: "Binary", value: "0b100" },
    { label: "Prime", value: "No" }
  ]
))
Card with copy button (Enter copies the result)
writeResponse(detail(
  '<div style="padding:16px;font-size:24px;color:#4FC3F7">42</div>',
  [{ label: "Expression", value: "6 * 7" }],
  [{ title: "Copy result", type: "copy", value: "42" }]
))
Card with multiple action buttons
writeResponse(detail(
  '<div style="padding:16px"><h2>Now Playing</h2><p>Mind Mirage — Windows 96</p></div>',
  [{ label: "Duration", value: "5:00" }],
  [
    { title: "Pause", type: "yoki_run", value: "sp pause", variant: "primary" },
    { title: "Next", type: "yoki_run", value: "sp next" },
    { title: "Copy track", type: "copy", value: "Mind Mirage — Windows 96" },
    { title: "Open in Spotify", type: "open_url", url: "https://open.spotify.com/track/..." }
  ]
))

list(items)

Shows a scrollable list of items. Each item has a title, subtitle, icon, and optional actions. Use for search results, bookmarks, file lists.

シグネチャ
list(items: [{
  id: string, title: string, subtitle?: string,
  icon?: string, actions?: Action[]
}, ...])
Simple list
writeResponse(list([
  { id: "1", title: "First result", subtitle: "Description here" },
  { id: "2", title: "Second result", subtitle: "Another description" },
]))
List with actions (Enter opens, Cmd+C copies)
writeResponse(list([
  {
    id: "gh",
    title: "GitHub",
    subtitle: "https://github.com  ·  Bookmarks Bar",
    icon: "🌐",
    actions: [
      { title: "Open", shortcut: "enter", type: "open_url", url: "https://github.com" },
      { title: "Copy URL", shortcut: "cmd+c", type: "copy", value: "https://github.com" }
    ]
  }
]))
Filtered list (search)
const input = await readInput()
const query = input.query.toLowerCase()

const allItems = [
  { id: "1", title: "GitHub", subtitle: "Code hosting" },
  { id: "2", title: "Google", subtitle: "Search engine" },
  { id: "3", title: "Gmail", subtitle: "Email" },
]

const filtered = query
  ? allItems.filter(i => i.title.toLowerCase().includes(query))
  : allItems

writeResponse(list(filtered))

background(hud, notification?)

Runs an action without showing any UI. Shows a brief HUD toast. Optionally sends a system notification. The user must press Enter to trigger it (never fires on keystroke). Use for play/pause, save, toggle.

シグネチャ
background(hud: string, notification?: { title: string, body: string })
Simple HUD toast
writeResponse(background("Done!"))
HUD + system notification
writeResponse(background(
  "Playing: Mind Mirage",
  { title: "Spotify", body: "Now playing: Mind Mirage — Windows 96" }
))

error(message, details?)

Shows an error message to the user. Use when something goes wrong — missing input, API failure, runtime not installed. Plugin crashes are also caught automatically.

シグネチャ
error(message: string, details?: string)
Simple error
writeResponse(error("Not found"))
Error with details
writeResponse(error("API request failed", "Check your internet connection and try again"))
Graceful handling of missing input
const input = await readInput()
if (!input.query) {
  writeResponse(error("No query", "Type something after the keyword"))
  return
}

stripKeyword(query, ...keywords)

Removes a keyword prefix from the query string. Useful when your plugin has subcommands (e.g. "search", "play", "list").

シグネチャ
stripKeyword(query: string, ...keywords: string[]): string
Remove subcommand
stripKeyword("search hello world", "search", "s")
// → "hello world"

stripKeyword("s hello world", "search", "s")
// → "hello world"  (matches short alias too)

stripKeyword("search", "search", "s")
// → ""  (exact match returns empty string)

escHtml(str)

Escapes HTML special characters (&, <, >, "). Use this when inserting user input into your HTML to prevent broken rendering or XSS.

シグネチャ
escHtml(s: string): string
Escape user input before rendering
const input = await readInput()
const safe = escHtml(input.query)
writeResponse(detail(`<div>${safe}</div>`))

// input: "<b>bold</b>"
// safe:  "&lt;b&gt;bold&lt;/b&gt;"
// renders as text, not HTML

プロトコル v2

ユーザーがキーワードを入力すると、YokiがスクリプトをV2Input JSONをstdinに書き込み、stdoutからV2Responseを読みます。非表示ウィンドウ、5秒タイムアウト(設定可能)。

stdin → V2Input
{
  "version": "2.0",
  "command": "greet",
  "action": "search",
  "query": "Ada",
  "context": {
    "yoki_version": "1.0.8.2",
    "os": "windows",
    "locale": "en",
    "plugin_dir": "C:/Users/you/yoki/plugins/hello",
    "data_dir":   "C:/Users/you/yoki/plugins/hello/data",
    "credentials": {}
  },
  "preferences": {}
}
stdout → V2Response
{
  "type": "detail",
  "markdown": "<div style=\"padding:16px\"><h2>Hello, Ada!</h2></div>",
  "metadata": [
    { "label": "Name", "value": "Ada" }
  ],
  "actions": [
    { "title": "Copy", "type": "copy", "value": "Hello, Ada!" }
  ]
}

V2Inputフィールド

version常に "2.0"
commandどのコマンドがトリガーされたか
action"search"(入力中), "execute"(Enter), "refresh"(自動更新)
queryキーワード後のユーザー入力全体
context.yoki_versionホストバージョン
context.os現在は常に "windows"
context.localeユーザーの言語
context.plugin_dirプラグインフォルダの絶対パス
context.data_dir隔離されたデータディレクトリ(実行間で保持)
context.credentialsクレデンシャルサービスからのキー(または空)

レスポンスモード

typeフィールドがYokiのレスポンス表示方法を決定します。

list
デフォルト

アイコン、タイトル、サブタイトル、アクション付きのスクロール可能なアイテム。

detail

メタデータサイドバーとアクションボタン付きのHTMLカード。Enterで最初のcopyアクションをコピー。

grid

列数設定可能なビジュアルグリッド。

background
副作用

UIなし — HUDトーストの副作用。Enterで起動。

form
近日公開

複数フィールド入力フォーム。近日公開。

error

オプション詳細付きエラー。プラグインクラッシュは自動的にキャッチ。

detail mode

writeResponse(detail(
  '<div style="padding:16px"><h2>Now Playing</h2><p>Mind Mirage — Windows 96</p></div>',
  [{ label: "Duration", value: "5:00" }, { label: "Album", value: "Enchanted Instrumentals" }],
  [{ title: "Pause", type: "yoki_run", value: "sp pause", variant: "primary" },
   { title: "Copy", type: "copy", value: "Mind Mirage" }]
))

list mode

writeResponse(list([
  { id: "1", title: "Mind Mirage", subtitle: "Windows 96 · 5:00", icon: "music",
    actions: [{ title: "Play", type: "yoki_run", value: "sp play spotify:track:xyz" },
              { title: "Copy", type: "copy", value: "Mind Mirage" }] }
]))

background mode

writeResponse(background("Playing: Mind Mirage", { title: "Spotify", body: "Now playing" }))

権限

プラグインはマニフェストで必要なものを宣言します。

network
HTTPS用ホスト名ホワイトリスト。
filesystem
プラグインデータディレクトリ相対の読み書きパス。隔離。
notifications
Windowsトースト通知。
clipboard
システムクリップボードの読み書き。

認証情報

プラグインはAPIキーを埋め込まないでください。必要なものを宣言するだけ。

マニフェスト宣言
{
  "name": "Spotify",
  "keyword": "sp",
  "protocol": "v2",
  "credentials": {
    "slug": "spotify",
    "service": "spotify",
    "keys": ["client_id"]
  },
  "commands": [...]
}
プラグインがコンテキストから読み取る
# Plugin reads credentials from V2Input.context — never embeds them.
import sys, json

inp = json.loads(sys.stdin.read())
client_id = (inp.get("context") or {}).get("credentials", {}).get("client_id")

if not client_id:
    # Yoki couldn't fetch creds (offline / not signed in) — surface a
    # graceful setup-needed response instead of crashing.
    print(json.dumps({"type": "error", "error": "Sign in to Yoki to use this plugin"}))
    sys.exit(0)

# ... rest of plugin uses client_id ...
PRを送信するか yokirun@yoki.run にメール。

マニフェストリファレンス

Yokiが理解する全フィールド。$schemaでIDE補完。

plugin.json — フルスキーマ
{
  "$schema":      "https://raw.githubusercontent.com/yoki-run/yoki/main/plugin.schema.json",
  "name":         "string  (required)  display name",
  "keyword":      "string  (required)  trigger word, e.g. 'sp'",
  "icon":         "string  icon name or emoji",
  "description":  "string  shown in Plugins Manager",
  "version":      "string  semver, e.g. '1.0.0'",
  "author":       "string",
  "homepage":     "string  url",
  "license":      "string  spdx, e.g. 'MIT'",
  "categories":   "string[] e.g. ['media', 'tools']",
  "yoki_min":     "string  min Yoki version, e.g. '1.0.8.0'",
  "protocol":     "'v2'",

  "permissions": {
    "network":       "string[]  hostname whitelist for HTTPS",
    "filesystem":    "string[]  'read:./data', 'write:./data'",
    "clipboard":     "boolean",
    "notifications": "boolean",
    "shell":         "boolean",
    "ai":            "boolean"
  },

  "credentials": {
    "slug":    "string  defaults to keyword",
    "service": "string  informational",
    "keys":    "string[] credential names"
  },

  "commands": [
    {
      "name":        "string  (required)  internal id",
      "title":       "string  (required)  user-facing label",
      "mode":        "'list' | 'detail' | 'grid' | 'background' | 'form'",
      "exec":        "string  (required)  script path relative to plugin dir",
      "keywords":    "string[]  sub-keyword aliases",
      "takes_query": "boolean   true = query passed verbatim",
      "refresh":     "number    ms — auto re-run interval",
      "timeout":     "number    ms — default 5000"
    }
  ]
}

アクションタイプ

ボタンやリストアイテムのaction.typeを設定:

copy  value値をクリップボードにコピー。
open_url  urlデフォルトブラウザでURLを開く。
yoki_run  valueYokiクエリを実行。
exec  exec, args別のプラグインスクリプトを実行。
paste  valueアクティブウィンドウにテキストを貼り付け。
notification  title, bodyシステムトースト通知を表示。
close  Yokiランチャーを閉じる。
refresh  現在のコマンドを再実行。

アクションアイコン

iconフィールドの3つの形式:

<svg ...>...</svg> インラインSVGマークアップ、DOMPurifyで消毒。
data:image/svg+xml;base64,... Data URLまたはhttps:// URL — <img>としてレンダリング。
"file" / "folder" / "music" / ... Yoki組み込みアイコンセットの名前。

プラグイン検出

Yokiは起動時とウィンドウフォーカス時に ~/yoki/plugins/ をスキャン。
plugin.jsonを持つ各サブディレクトリが1つのプラグイン。
プラグインは隔離されたdata/ディレクトリを取得。
読み込み失敗はプラグインマネージャーに表示。
重複キーワードは拒否。

ローカルテスト

# Yokiなしでプラグインをテスト
echo '{"query":"test","command":"main","context":{}}' | node main.js

ショーケース

JavaScript SDKで構築された公式プラグイン。どれでもクローンして始められます。

Spotify · Node.js · sp

Spotify完全制御 — 再生中、検索、再生、デバイス、プレイリスト。

sp  Now Playing card
sp play [query]  Search & play
sp pause / next / prev  Playback controls
sp s [query]  Search tracks
sp d  Devices
sp pl  Playlists
機能
  • OAuth 2.0 PKCE in pure stdlib
  • Auto-refresh detail card
  • Inline SVG icons
  • Credentials service integration
Extended Math · Node.js · math

高度な計算機 — 三角関数、積分、微分、ASCIIプロット。

math 2^8 + sqrt(144)  Calculate
math trig table 60  Trig table
math int x^2 0 10  Integrate
math der sin(x^2)  Derivative
math plot sin(x) -pi pi  ASCII plot
機能
  • Safe expression parser (no eval)
  • Symbolic differentiation (chain rule)
  • Numerical integration (Simpson)
  • ASCII function plots
Bookmarks · Node.js · bm

Chrome, Edge, Brave, Vivaldiのブックマークを即座に検索。

bm  List all bookmarks
bm github  Search bookmarks
機能
  • Reads local bookmark files (no API)
  • Multi-browser detection
  • Multi-word fuzzy search
  • Open in browser or copy URL

次のステップ

プラグインシステムは本番対応済み。

完了
v2 protocol + 5 response modes
stdin/stdout JSON、全レスポンスモード、アクションボタン、自動更新。
完了
Bundled JavaScript SDK
Yokiバイナリに埋め込まれたゼロインストールSDK。
完了
Plugin Marketplace
アプリ内ブラウズ + ワンクリックインストール。
完了
Credentials service
プラグインが必要なものを宣言、Yokiがランタイムでシークレットを取得。
完了
Scaffold CLI (npx create-yoki-plugin)
インタラクティブscaffold — npx create-yoki-pluginを実行、JavaScriptプラグインを数秒で作成。
完了
JSON Schema for plugin.json
マニフェスト全フィールドのIDE自動補完。
Plugin settings UI (form mode)
プラグインが設定フィールドを宣言、Yokiが設定ページをレンダリング。
Auto-update for plugins
インストール済みバージョンとmarketplaceタグを比較、更新バッジ。
後で
AI tool integration
プラグインがYoki AIが直接呼び出せるツールとして公開。
最初のプラグインを作成
SDK安定版、3つのリファレンスプラグイン。
始める
Yoki Plugin SDK · v2 · yoki.run