.agents/skills/wox-plugin-creator/references/sdk_python.md
Wox requires Python 3.10 or later.
uv add wox-plugin
from wox_plugin import Plugin, Query, QueryResponse, Result, Context, PluginInitParams
class MyPlugin(Plugin):
async def init(self, ctx: Context, params: PluginInitParams) -> None:
self.api = params.api
async def query(self, ctx: Context, query: Query) -> QueryResponse:
return QueryResponse(results=[])
Return QueryResponse when plugin.json declares MinWoxVersion >= 2.0.4.
Use QueryResponse.layout.result_preview_width_ratio and
QueryResponse.layout.grid_layout for query-scoped layout. The older
resultPreviewWidthRatio and gridLayout metadata features are deprecated
because they can only describe static plugin or command defaults.
class Query:
type: str # "input" or "selection"
raw_query: str
trigger_keyword: str
command: str
search: str
class Result:
title: str # Supports "i18n:key" prefix for auto-translation
icon: WoxImage
sub_title: str = "" # Supports "i18n:key" prefix
actions: List[ResultAction] = []
score: float = 0.0
context_data: Any = None
class WoxImage:
# Factory methods
@classmethod
def new_emoji(cls, char: str) -> "WoxImage"
@classmethod
def new_absolute(cls, path: str) -> "WoxImage"
@classmethod
def new_relative(cls, path: str) -> "WoxImage"
All methods are async and require ctx.
change_query(ctx, query: PlainQuery): Update search bar.hide_app(ctx): Hide Wox.show_app(ctx): Show Wox.notify(ctx, message): Show notification.log(ctx, level, msg): Write log. Levels: "Info", "Error".copy(ctx, params: CopyParams): Copy text/image.is_visible(ctx): Check visibility.If the plugin needs on-disk cache, prefer get_cache_folder over any custom directory.
get_cache_folder(ctx): Return ~/.wox/cache/plugins/<plugin-id>/. Wox creates it if needed and deletes it on uninstall.init(), keep the path, and write downloads, thumbnails, and search-result files under it.cache/, tmp/, or downloads/ next to the plugin file, under user data, or under a hardcoded folder name.get_setting / set_setting for those.Prefer these APIs for all plugin settings. Values stored here can sync across machines through Wox cloud sync. Do not persist ordinary settings in local files or a custom store.
get_setting(ctx, key): Get setting.save_setting(ctx, key, value, is_platform_specific): Save setting. Normal plugin settings are eligible for cloud sync, so pass True for platform-only values such as local paths, executable paths, shell commands, hotkeys, browser profiles, application paths, and system integrations.on_setting_changed(ctx, callback): Listen for changes.on_get_dynamic_setting(ctx, callback): Provide runtime-generated setting definitions for dynamic settings.update_result(ctx, result: UpdatableResult): Real-time update.push_results(ctx, query, results): Append results.refresh_query(ctx, param): Re-run query.get_updatable_result(ctx, result_id): Get current result state.ai_chat_stream(ctx, model, convs, options, callback): Stream LLM response.get_translation(ctx, key): Get raw translated string.
Note: Returns raw string. Use f-strings or
.format()for parameter substitution.
get_setting, save_setting, and on_setting_changed for plugin settings. These APIs participate in Wox cloud sync across machines. Avoid local files or custom persistence for values the user would expect to follow them to another device.get_cache_folder(ctx) first. Do not invent a cache directory under the plugin folder or user-data tree.create_textbox_setting()create_checkbox_setting()create_label_setting()create_select_setting() helper today.select, table, validators, or dynamic, construct PluginSettingDefinitionItem and the corresponding value objects directly, or emit the expected JSON shape manually.plugin.json and validator shape, read references/plugin_json_schema.md.references/settings_patterns.md.save_setting(ctx, key, value, is_platform_specific) calls to the setting metadata. Do not hardcode False for dynamically saved settings if their SettingDefinitions entry uses IsPlatformSpecific: true.DisabledInPlatforms only controls where the setting is disabled; it does not isolate cloud-synced values.QueryRequirements in plugin.json when a query requires settings such as API keys. Wox blocks the query before calling query() and shows the built-in query_requirement_settings setup preview.register_query_requirements API. Declare query requirements in metadata.from dataclasses import dataclass, field
@dataclass
class PluginQueryRequirement:
setting_key: str
validators: list[dict] = field(default_factory=list)
message: str = ""
@dataclass
class PluginQueryRequirements:
any_query: list[PluginQueryRequirement] = field(default_factory=list)
query_without_command: list[PluginQueryRequirement] = field(default_factory=list)
query_with_command: dict[str, list[PluginQueryRequirement]] = field(default_factory=dict)
Metadata example:
{
"SettingDefinitions": [
{
"Type": "textbox",
"Value": {
"Key": "accessKey",
"Label": "i18n:access_key",
"DefaultValue": "",
"Validators": [{ "Type": "not_empty", "Value": {} }]
}
}
],
"QueryRequirements": {
"AnyQuery": [
{
"SettingKey": "accessKey",
"Message": "i18n:access_key_required"
}
],
"QueryWithoutCommand": [],
"QueryWithCommand": {}
}
}
from wox_plugin import (
PluginSettingDefinitionItem,
PluginSettingDefinitionType,
PluginSettingValueLabel,
)
async def _on_get_dynamic_setting(ctx, key):
if key == "separator_preview":
return PluginSettingDefinitionItem(
type=PluginSettingDefinitionType.LABEL,
value=PluginSettingValueLabel(content="Preview: 1,234.56"),
)
return PluginSettingDefinitionItem(
type=PluginSettingDefinitionType.LABEL,
value=PluginSettingValueLabel(content="Unknown setting"),
)
from wox_plugin import Plugin, Query, Result, WoxImage
class HelloPlugin(Plugin):
async def init(self, ctx, params): self.api = params.api
async def query(self, ctx, query):
# I18n with formatting
raw_fmt = await self.api.get_translation(ctx, "hello_format") # "Hello {name}"
title = raw_fmt.format(name=query.search)
return [Result(
title=title,
icon=WoxImage.new_emoji("👋"),
actions=[]
)]
plugin = HelloPlugin()