### Run Plugin SDK Tests
Source: https://github.com/lilimozi/openhanako/blob/main/examples/plugins/sdk-showcase/README.md
Execute tests for the plugin SDK examples from the repository root.
```bash
npm test -- tests/plugin-sdk-examples.test.ts
```
--------------------------------
### Basic Plugin UI with Components
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGIN_SDK.md
Create a simple plugin panel using components from `@hana/plugin-components`. This example demonstrates setting up a theme provider and a card with a button.
```tsx
import { Button, CardShell, HanaThemeProvider } from '@hana/plugin-components';
import '@hana/plugin-components/styles.css';
export function Panel() {
return (
);
}
```
--------------------------------
### Development Commands for OpenHanako
Source: https://github.com/lilimozi/openhanako/blob/main/README_EN.md
Common commands for installing dependencies, starting the application in different modes, running tests, and type checking.
```bash
npm install
```
```bash
npm start
```
```bash
npm run start:vite
```
```bash
npm run server
```
```bash
npm run cli
```
```bash
npm test
```
```bash
npm run typecheck
```
--------------------------------
### Example Commit Message Format
Source: https://github.com/lilimozi/openhanako/blob/main/skills2set/skill-creator/SKILL.md
An example of how to format commit messages, including a feature addition.
```markdown
## Commit message format
**Example 1:**
Input: Added user authentication with JWT tokens
Output: feat(auth): implement JWT-based authentication
```
--------------------------------
### Configuration Schema Example
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS_EN.md
An example of a JSON Schema for plugin configuration, defining properties like 'interval' and 'sound' with their types and default values. Configuration is managed via `ctx.config`.
```json
{
"contributes": {
"configuration": {
"properties": {
"interval": { "type": "number", "default": 25, "title": "Work interval (minutes)" },
"sound": { "type": "boolean", "default": true, "title": "Completion sound" }
}
}
}
}
```
--------------------------------
### Launch Evaluation Viewer
Source: https://github.com/lilimozi/openhanako/blob/main/skills2set/skill-creator/SKILL.md
Start the evaluation viewer to review qualitative and quantitative results. It can be launched in the background.
```bash
nohup python /eval-viewer/generate_review.py \
/iteration-N \
--skill-name "my-skill" \
--benchmark /iteration-N/benchmark.json \
> /dev/null 2>&1 &
VIEWER_PID=$!
```
--------------------------------
### Full Plugin Runtime Example
Source: https://github.com/lilimozi/openhanako/blob/main/packages/plugin-runtime/README.md
Demonstrates the usage of various helper functions for creating agents, sessions, sampling text, sending messages, subscribing to events, generating images and media, and transcribing audio within a plugin's onload function.
```typescript
import {
createAgent,
createSession,
generateMedia,
generateImage,
transcribeAudio,
sampleText,
sendSessionMessage,
subscribeSessionEvents,
} from '@hana/plugin-runtime';
export default definePlugin({
async onload(ctx, { register }) {
const agent = await createAgent(ctx, {
name: 'Tavern Character',
visibility: 'plugin_private',
memoryPolicy: { enabled: true },
});
const session = await createSession(ctx, {
agentId: (agent as any).agent.id,
kind: 'tavern',
visibility: 'plugin_private',
cwd: ctx.dataDir,
});
const query = await sampleText(ctx, {
operation: 'tavern-rag-query',
messages: [{ role: 'user', content: 'Extract world-lore keywords for this turn' }],
maxTokens: 80,
});
await sendSessionMessage(ctx, (session as any).sessionPath, {
text: 'I push the door open.',
context: {
beforeUser: [
{ label: 'world', text: 'Rainy city night; the old theater is still open.' },
{ label: 'rag_query', text: (query as any).text },
],
},
});
register(subscribeSessionEvents(ctx, (session as any).sessionPath, (event) => {
ctx.log.info('session event', event);
}));
await generateImage(ctx, {
sessionPath: (session as any).sessionPath,
prompt: 'A handwritten character card on warm paper',
referenceImages: [
{ kind: 'session_file', fileId: 'sf_reference_a' },
{ kind: 'session_file', fileId: 'sf_reference_b' },
],
ratio: '3:2',
});
await generateMedia(ctx, {
kind: 'video',
sessionPath: (session as any).sessionPath,
prompt: 'A slow page-turn animation on warm paper',
});
const transcription = await transcribeAudio(ctx, {
sessionPath: (session as any).sessionPath,
fileId: 'session-file-id',
});
ctx.log.info('transcription', transcription);
},
});
```
--------------------------------
### Agent Template Example
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS_EN.md
Defines a basic agent with a name, system prompt, default model, and default tools.
```json
{
"name": "Translator",
"systemPrompt": "You are a translator.",
"defaultModel": "gpt-4o",
"defaultTools": ["web-search"]
}
```
--------------------------------
### Basic Plugin Panel Example
Source: https://github.com/lilimozi/openhanako/blob/main/packages/plugin-components/README.md
Demonstrates the basic structure of a plugin panel using various components from @hana/plugin-components. Ensure styles are imported for proper rendering.
```tsx
import {
Button,
CardShell,
HanaThemeProvider,
SettingRow,
Switch,
} from '@hana/plugin-components';
import '@hana/plugin-components/styles.css';
export function PluginPanel() {
return (
}
/>
);
}
```
--------------------------------
### Initialize and Interact with Hana SDK
Source: https://github.com/lilimozi/openhanako/blob/main/packages/plugin-sdk/README.md
Initializes the SDK, accesses assets, resizes the UI, and shows a toast message. This snippet demonstrates basic SDK setup and common interactions.
```typescript
import { hana } from '@hana/plugin-sdk';
hana.ready();
const logoUrl = hana.assets.url('images/logo.svg');
hana.ui.resize({ height: 320 });
await hana.toast.show({ message: 'Saved', type: 'success' });
await hana.external.open('https://example.com');
await hana.clipboard.writeText('Copied text');
```
--------------------------------
### Initialize Hana SDK and Show Toast
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGIN_SDK.md
Import and initialize the Hana SDK, then display a toast message. This is a basic setup for interacting with Hana's core functionalities.
```typescript
import { hana } from '@hana/plugin-sdk';
hana.ready();
hana.ui.resize({ height: 320 });
await hana.toast.show({ message: 'Ready' });
```
--------------------------------
### Full Plugin Manifest Example
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS_EN.md
A comprehensive manifest file detailing plugin ID, version, trust level, activation events, capabilities, UI host capabilities, contribution schema, and dependencies.
```json
{
"manifestVersion": 1,
"id": "my-plugin",
"name": "My Plugin",
"version": "1.0.0",
"description": "What this plugin does",
"trust": "full-access",
"activationEvents": ["onToolCall:search"],
"capabilities": ["session", "agent", "model.sample", "media.generate"],
"sensitiveCapabilities": ["filesystem.write"],
"ui": {
"hostCapabilities": ["external.open"]
},
"contributes": {
"configuration": { ... }
},
"depends": {
"capabilities": ["bridge:send"]
}
}
```
--------------------------------
### Scaffold a Beginner Direct Plugin
Source: https://github.com/lilimozi/openhanako/blob/main/skills2set/hana-plugin-creator/SKILL.md
Generate a basic Hana plugin using the 'direct' template, suitable for beginners. This command creates a plugin with no npm install or build step required.
```bash
python3 skills2set/hana-plugin-creator/scripts/create_hana_plugin.py "My Plugin" --path examples/plugins --audience beginner --template direct
```
--------------------------------
### Request-Level Context Example
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS_EN.md
Demonstrates accessing the request-level context ('pluginRequestContext') to interact with the session bus, such as creating a new session.
```javascript
app.post("/create-session", async (c) => {
const reqCtx = c.get("pluginRequestContext");
// reqCtx.principal identity behind this request (owner device / this plugin's iframe surface…), null when invoked directly in tests
// reqCtx.agentId current agent id injected by the proxy layer
// reqCtx.capabilityGrant { accessLevel, declaredPermissions, legacyDeclaration }
const result = await reqCtx.bus.request("session:create", { agentId: reqCtx.agentId });
return c.json(result);
});
```
--------------------------------
### Hello Tool Plugin Example
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS_EN.md
A basic JavaScript tool plugin that responds to a 'hello' command with a personalized greeting. It defines the tool's name, description, parameters, and execution logic.
```javascript
// tools/hello.js
export const name = "hello";
export const description = "Say hello to someone";
export const parameters = {
type: "object",
properties: { name: { type: "string" } },
required: ["name"],
};
export async function execute(input) {
return `Hello, ${input.name}!`;
}
```
--------------------------------
### Plugin Panel Component
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS_EN.md
Example of a Plugin Panel component using HanaThemeProvider, CardShell, SettingRow, Switch, and Button from @hana/plugin-components. Ensure @hana/plugin-components/styles.css is imported.
```javascript
import { Button, CardShell, HanaThemeProvider, SettingRow, Switch } from "@hana/plugin-components";
import "@hana/plugin-components/styles.css";
export function PluginPanel() {
return (
} />
);
}
```
--------------------------------
### GET /webhook
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS.md
A webhook endpoint that demonstrates accessing plugin context via Hono middleware.
```APIDOC
## GET /webhook
### Description
A webhook endpoint that demonstrates accessing plugin context via Hono middleware.
### Method
GET
### Endpoint
/api/plugins/{pluginId}/webhook
### Response
#### Success Response (200)
- **ok** (boolean) - Indicates success.
- **pluginId** (string) - The ID of the plugin.
#### Response Example
{
"ok": true,
"pluginId": "your-plugin-id"
}
```
--------------------------------
### Register a Background Task Instance
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS_EN.md
Register a task instance every time a background task starts. This allows HanaAgent to track and manage the task.
```js
await this.ctx.bus.request("task:register", {
taskId: "my-task-123",
type: "my-task-type",
parentSessionPath: sessionPath,
meta: { type: "my-task", prompt: "..." },
});
```
--------------------------------
### Reference Host-Provided Theme CSS
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS.md
Plugins can reference the host-provided theme CSS to maintain visual consistency by dynamically getting the 'hana-css' parameter from the URL.
```html
```
--------------------------------
### Initialize Plugin SDK and Send Host Requests
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS.md
Use the @hana/plugin-sdk to initialize the plugin, resize the UI, display toasts, open external links, and interact with the clipboard. Ensure necessary capabilities are declared in the manifest.
```javascript
import { hana } from '@hana/plugin-sdk';
hana.ready();
hana.ui.resize({ height: 320 });
await hana.toast.show({ message: '已刷新', type: 'success' });
await hana.external.open('https://example.com');
await hana.clipboard.writeText('复制内容');
```
--------------------------------
### Initialize Plugin SDK and Host Interactions
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS_EN.md
Use the @hana/plugin-sdk to signal readiness, resize the plugin iframe, display toasts, open external links, and interact with the clipboard. Ensure necessary host capabilities are declared in the manifest.
```javascript
import { hana } from '@hana/plugin-sdk';
hana.ready();
hana.ui.resize({ height: 320 });
await hana.toast.show({ message: 'Refreshed', type: 'success' });
await hana.external.open('https://example.com');
await hana.clipboard.writeText('Copied text');
```
--------------------------------
### Initialize and Render Benchmark
Source: https://github.com/lilimozi/openhanako/blob/main/skills2set/skill-creator/eval-viewer/viewer.html
Calls the initialization function and then the renderBenchmark function to display the benchmark results. This is the main entry point for rendering.
```javascript
init(); renderBenchmark();
```
--------------------------------
### GET /status
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS.md
Retrieves the plugin's status, including its ID, using the register export syntax.
```APIDOC
## GET /status
### Description
Retrieves the plugin's status, including its ID.
### Method
GET
### Endpoint
/api/plugins/{pluginId}/status
### Response
#### Success Response (200)
- **pluginId** (string) - The ID of the plugin.
#### Response Example
{
"pluginId": "your-plugin-id"
}
```
--------------------------------
### Build Packages
Source: https://github.com/lilimozi/openhanako/blob/main/examples/plugins/sdk-showcase/README.md
Build the necessary packages for the plugin SDK showcase.
```bash
npm run build:packages
```
--------------------------------
### Get Asset URL
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGIN_SDK.md
Generate a URL for a file bundled within the plugin's assets directory. Use this for referencing images, scripts, or other static files in the browser.
```typescript
const logoUrl = hana.assets.url('images/logo.svg');
```
--------------------------------
### Class-based Stateful Plugin with Lifecycle Methods
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS_EN.md
The traditional class-based plugin form is supported, allowing explicit definition of onload and onunload methods for resource management and setup.
```javascript
import { HANA_BUS_SKIP } from "@hana/plugin-runtime";
export default class MyPlugin {
async onload() {
// ctx is injected by PluginManager:
// this.ctx.bus — EventBus (full: emit/subscribe/request/handle)
// this.ctx.config — Config read/write (get/set)
// this.ctx.dataDir — Private data directory path
// this.ctx.log — Logger with pluginId prefix
// this.ctx.pluginId — Plugin ID
// this.ctx.pluginDir — Plugin installation directory
// this.ctx.registerTool — Dynamic tool registration (returns cleanup function)
// Resources registered via register() are auto-cleaned on unload (reverse order)
this.register(
this.ctx.bus.handle("bridge:send", async (payload) => {
if (payload.platform !== "feishu") return HANA_BUS_SKIP;
await this.sendToFeishu(payload);
return { sent: true };
})
);
this.ws = await this.connect();
}
async onunload() {
// Resources from register() are auto-cleaned, no manual unhandle needed
// Only clean up things the framework can't manage
this.ws?.close();
}
}
```
--------------------------------
### Create Agent and Session
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS_EN.md
Demonstrates creating a private agent and a private session for a specific kind.
```javascript
import {
createAgent,
createSession,
generateMedia,
generateImage,
transcribeAudio,
sampleText,
sendSessionMessage,
} from "@hana/plugin-runtime";
const agent = await createAgent(ctx, {
name: "Tavern Character",
visibility: "plugin_private",
memoryPolicy: { enabled: true },
});
const session = await createSession(ctx, {
agentId: agent.agent.id,
kind: "tavern",
visibility: "plugin_private",
cwd: ctx.dataDir,
});
```
--------------------------------
### Generate Image with Response Delivery
Source: https://github.com/lilimozi/openhanako/blob/main/packages/plugin-runtime/README.md
Example of generating an image using the `generateImage` helper with `delivery: { mode: 'response' }`. This mode omits `sessionPath` and requires polling for task completion.
```typescript
const result = await generateImage(ctx, {
prompt: 'A small icon on transparent background',
delivery: { mode: 'response' },
});
```
--------------------------------
### Provider Plugin with Local CLI Runtime
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS.md
Configure a provider that runs a local command-line interface for media generation. It specifies the command, arguments, and output pattern.
```javascript
export const id = "my-image-cli";
export const displayName = "My Image CLI";
export const authType = "none";
export const runtime = {
kind: "local-cli",
protocolId: "local-cli-media",
command: {
executable: "my-image-cli",
args: [
{ literal: "generate" },
{ option: "--prompt", from: "prompt" },
{ option: "--model", from: "modelId" },
{ option: "--output", from: "outputDir" },
],
timeoutMs: 120000,
output: { kind: "file_glob", directory: "outputDir", pattern: "*.png" },
},
};
export const capabilities = {
chat: { projection: "none" },
media: {
imageGeneration: {
models: [
{
id: "my-image-model",
displayName: "My Image Model",
protocolId: "local-cli-media",
inputs: ["text"],
outputs: ["image"],
},
],
},
},
};
```
--------------------------------
### Runtime Session and Agent API Usage
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGIN_SDK.md
Demonstrates how to create an agent and a session, send a message with contextual information, and subscribe to session events using the `@hana/plugin-runtime` SDK. Use `visibility: 'plugin_private'` to keep plugin-owned agents and sessions out of default lists.
```javascript
import {
createAgent,
createSession,
getAgentProfile,
listSessions,
sendSessionMessage,
subscribeSessionEvents,
} from '@hana/plugin-runtime';
const agent = await createAgent(ctx, {
name: 'Tavern Character',
visibility: 'plugin_private',
memoryPolicy: { enabled: true },
});
const session = await createSession(ctx, {
agentId: agent.agent.id,
kind: 'tavern',
visibility: 'plugin_private',
memoryEnabled: true,
cwd: ctx.dataDir,
});
await sendSessionMessage(ctx, session.sessionPath, {
text: 'Hello',
context: {
beforeUser: [
{ label: 'world', text: 'The city is under moonlit rain.' },
{ label: 'mood', text: 'Keep the reply intimate and quiet.' },
],
},
});
const off = subscribeSessionEvents(ctx, session.sessionPath, (event) => {
ctx.log.debug('session event', event);
});
```
--------------------------------
### Extension API for Event Interception
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS.md
Intercept LLM call chain events using the ExtensionAPI provided by the Pi SDK. This example removes empty tools from the payload before a provider request.
```javascript
// extensions/strip-empty-tools.js
export default function(pi) {
pi.on("before_provider_request", (event) => {
const p = event.payload;
if (p && Array.isArray(p.tools) && p.tools.length === 0) {
delete p.tools;
}
return p;
});
}
```
--------------------------------
### Stage and Deliver Local Files
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS_EN.md
Stage a local file as a SessionFile and return it in details.media.items for framework delivery. Ensure local files are registered via stageFile() and not bypassed.
```javascript
import { createMediaDetails } from "@hana/plugin-runtime";
const staged = toolCtx.stageFile({
sessionPath: toolCtx.sessionPath,
filePath: "/path/to/image.png",
label: "image.png",
});
return {
content: [{ type: "text", text: "Image generated" }],
details: createMediaDetails([staged]),
};
```
--------------------------------
### SKILL.md File Structure
Source: https://github.com/lilimozi/openhanako/blob/main/skills2set/skill-creator/SKILL.md
Illustrates the basic directory structure for a skill, including the required SKILL.md file and optional bundled resources like scripts, references, and assets.
```markdown
skill-name/
├── SKILL.md (required)
│ ├── YAML frontmatter (name, description required)
│ └── Markdown instructions
└── Bundled Resources (optional)
├── scripts/ - Executable code for deterministic/repetitive tasks
├── references/ - Docs loaded into context as needed
└── assets/ - Files used in output (templates, icons, fonts)
```
--------------------------------
### Scaffold a Developer Professional React Plugin
Source: https://github.com/lilimozi/openhanako/blob/main/skills2set/hana-plugin-creator/SKILL.md
Generate a Hana plugin with a professional React setup, including Vite and SDK starter. This option is for developers who expect package scripts and typed UI code.
```bash
python3 skills2set/hana-plugin-creator/scripts/create_hana_plugin.py "My Plugin" --path examples/plugins --audience developer --template professional-react --sdk-mode workspace
```
--------------------------------
### Correct Session Operations in Hana Plugins
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS_EN.md
Demonstrates the correct way to perform session operations like sending messages and aborting sessions by explicitly including the sessionPath. Also shows how to attach per-turn context.
```javascript
// Correct: explicitly specify sessionPath and attach per-turn context if needed
await bus.request("session:send", {
text: "Hello",
sessionPath: "/path/to/session.jsonl",
context: {
beforeUser: [{ label: "world", text: "The character is in a quiet archive." }],
},
});
await bus.request("session:abort", {
sessionPath: "/path/to/session.jsonl",
});
```
```javascript
// Wrong: omitting sessionPath may target the wrong session under concurrency
await bus.request("session:send", { text: "Hello" });
```
--------------------------------
### Package Skill
Source: https://github.com/lilimozi/openhanako/blob/main/skills2set/skill-creator/SKILL.md
Use this command to package a skill folder into a .skill file. This requires the `stage_files` tool to be available.
```bash
python -m scripts.package_skill
```
--------------------------------
### List and Subscribe to Usage Events
Source: https://github.com/lilimozi/openhanako/blob/main/packages/plugin-runtime/README.md
Demonstrates how to list past LLM usage entries and subscribe to new usage events using `listUsageEntries` and `subscribeUsageEvents` helpers. Requires `usage.read` capability.
```typescript
import { definePlugin, listUsageEntries, subscribeUsageEvents } from '@hana/plugin-runtime';
export default definePlugin({
async onload(ctx, { register }) {
const usage = await listUsageEntries(ctx, {
since: '2026-05-01T00:00:00.000Z',
limit: 100,
});
ctx.log.info('usage records', usage.entries.length);
register(subscribeUsageEvents(ctx, (entry, meta) => {
ctx.log.info('new usage entry', entry.requestId, meta.sessionPath);
}));
},
});
```
--------------------------------
### Media API - SDK Helpers
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGIN_SDK.md
The Media API provides SDK helpers for interacting with configured media providers. Functions like `listMediaProviders`, `resolveMediaModel`, `generateImage`, `generateVideo`, `generateMedia`, and `transcribeAudio` allow plugins to manage and generate media content.
```APIDOC
## Media SDK Helpers
### Description
Provides functions to interact with Hana's media provider stack for listing providers, resolving models, generating images/videos, and transcribing audio.
### Available Functions
* `listMediaProviders()`: Reads configured image/video/speech-capable providers.
* `resolveMediaModel(modelId)`: Resolves a specific media model.
* `generateImage(ctx, options)`: Submits an image generation task.
* `generateVideo(ctx, options)`: Submits a video generation task.
* `generateMedia(ctx, options)`: Submits a general media generation task.
* `transcribeAudio(ctx, options)`: Transcribes audio input.
### `generateImage` Options
* `sessionPath` (string, Optional): Path for session files. Omit when `delivery.mode` is 'response'.
* `prompt` (string): The prompt for image generation.
* `referenceImages` (Array<{ kind: string, fileId: string }>, Optional): References to images for generation.
* `ratio` (string, Optional): The aspect ratio of the generated image.
* `resolution` (string, Optional): The resolution of the generated image.
* `delivery` ({ mode: string }): Specifies the delivery mode. If 'response', `sessionPath` can be omitted and no `SessionFile` records are created.
### `transcribeAudio` Options
* `sessionPath` (string): Required for ASR tasks.
* `fileId` (string): Required for ASR tasks.
### Response for `transcribeAudio`
* `ok` (boolean): Indicates success.
* `transcription` (string): The transcribed text.
```
--------------------------------
### Media API - Direct HTTP Endpoints
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGIN_SDK.md
Plugins with host HTTP credentials can directly submit requests to the native media facade endpoints for media generation and audio transcription.
```APIDOC
## Native Media Facade Endpoints
### Description
Direct HTTP endpoints for submitting media generation and audio transcription tasks.
### Endpoints
* `POST /api/media/generate`
* `POST /api/media/image/generate`
* `POST /api/media/video/generate`
* `POST /api/media/asr/transcribe`
### Common Parameters
* `prompt` (string): Required for image/video generation.
* `sessionPath` (string): Required for image/video generation with `delivery.mode = "session"` and for all ASR tasks.
* `delivery` ({ mode: string }): Specifies the delivery mode. Defaults to 'session'. If 'response', `sessionPath` can be omitted for image/video requests.
### Request Body Example (Image Generation with Response Delivery)
```json
{
"prompt": "A small icon on transparent background",
"delivery": { "mode": "response" }
}
```
### Request Body Example (ASR Transcription)
```json
{
"sessionPath": "path/to/session",
"fileId": "sf_audio_file"
}
```
### Notes
* Image reference fields accept only `SessionFile` references (e.g., `{ "kind": "session_file", "fileId": "sf_..." }`).
* These routes require chat-scope host credentials.
```
--------------------------------
### Create Hana Plugin Scaffold
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS_EN.md
A bash command to scaffold a new Hana plugin using the hana-plugin-creator tool. It specifies the plugin name, output path, and kind (e.g., 'full' access).
```bash
python3 skills2set/hana-plugin-creator/scripts/create_hana_plugin.py "My Plugin" --path examples/plugins --kind full
```
--------------------------------
### Define a Tool to Render an Image
Source: https://github.com/lilimozi/openhanako/blob/main/packages/plugin-runtime/README.md
Use `defineTool` to create a tool that stages a local file and returns media details. The `stageFile` function is used for plugin-generated local files, and `createMediaDetails` normalizes file information for various clients.
```typescript
import { createMediaDetails, defineTool } from '@hana/plugin-runtime';
export const renderImage = defineTool({
name: 'render_image',
description: 'Render an image',
async execute(_input, ctx) {
const staged = ctx.stageFile?.({
sessionPath: ctx.sessionPath,
filePath: '/absolute/path/to/image.png',
label: 'image.png',
});
if (!staged) throw new Error('stageFile unavailable');
return {
content: [{ type: 'text', text: 'Image generated' }],
details: createMediaDetails([staged]),
};
},
});
```
--------------------------------
### Define a Tool and Plugin
Source: https://github.com/lilimozi/openhanako/blob/main/packages/plugin-runtime/README.md
Defines a 'search' tool with parameters and registers it within a plugin's lifecycle. Use this to create reusable tools that can be invoked by the Hana runtime.
```typescript
import {
definePlugin,
defineTool
} from '@hana/plugin-runtime';
export const searchTool = defineTool({
name: 'search',
description: 'Search project data',
parameters: {
type: 'object',
properties: {
query: { type: 'string' },
},
required: ['query'],
},
async execute(input, ctx) {
ctx.log.info('searching', input);
return `results for ${input.query}`;
},
});
export default definePlugin({
async onload(ctx, { register }) {
if (ctx.registerTool) {
register(ctx.registerTool(searchTool));
}
},
});
```
--------------------------------
### Define Local CLI Media Provider
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGIN_SDK.md
Use defineProvider for TypeScript-friendly authoring of a local CLI media provider. Specify command details and argument mapping.
```javascript
import { defineProvider } from '@hana/plugin-runtime';
const provider = defineProvider({
id: 'my-image-cli',
displayName: 'My Image CLI',
authType: 'none',
runtime: {
kind: 'local-cli',
protocolId: 'local-cli-media',
command: {
executable: 'my-image-cli',
args: [
{ literal: 'generate' },
{ option: '--prompt', from: 'prompt' },
{ option: '--model', from: 'modelId' },
{ option: '--output', from: 'outputDir' },
],
timeoutMs: 120000,
},
});
```
--------------------------------
### Domain-Specific Skill Organization
Source: https://github.com/lilimozi/openhanako/blob/main/skills2set/skill-creator/SKILL.md
Demonstrates how to organize skills that support multiple domains or frameworks by creating separate reference files for each, ensuring Claude reads only the relevant documentation.
```markdown
cloud-deploy/
├── SKILL.md (workflow + selection)
└── references/
├── aws.md
├── gcp.md
└── azure.md
```
--------------------------------
### Run Skill Optimization Loop
Source: https://github.com/lilimozi/openhanako/blob/main/skills2set/skill-creator/SKILL.md
Execute the optimization loop using a Python script. This process splits the eval set, evaluates the current description, and iteratively proposes improvements.
```bash
python -m scripts.run_loop \
--eval-set \
--skill-path \
--model \
--max-iterations 5 \
--verbose
```
--------------------------------
### Lazy Activation with Activation Events
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS_EN.md
Configure a plugin to activate on specific events, such as a tool call, to defer execution until needed.
```json
{
"id": "lazy-search",
"trust": "full-access",
"activationEvents": ["onToolCall:search"],
"contributes": {
"page": { "title": "Search", "route": "/search" }
}
}
```
--------------------------------
### Define a Provider for Image Generation
Source: https://github.com/lilimozi/openhanako/blob/main/packages/plugin-runtime/README.md
Use `defineProvider` to register a local CLI provider for image generation. This includes specifying the command to execute, arguments, and expected output. Ensure chat and media capabilities are explicitly defined.
```typescript
import { defineProvider } from '@hana/plugin-runtime';
const provider = defineProvider({
id: 'my-image-cli',
displayName: 'My Image CLI',
authType: 'none',
runtime: {
kind: 'local-cli',
protocolId: 'local-cli-media',
command: {
executable: 'my-image-cli',
args: [
{ literal: 'generate' },
{ option: '--prompt', from: 'prompt' },
{ option: '--model', from: 'modelId' },
{ option: '--output', from: 'outputDir' },
],
timeoutMs: 120_000,
output: { kind: 'file_glob', directory: 'outputDir', pattern: '*.png' },
},
},
capabilities: {
chat: { projection: 'none' },
media: {
imageGeneration: {
models: [
{
id: 'my-image-model',
displayName: 'My Image Model',
protocolId: 'local-cli-media',
inputs: ['text'],
outputs: ['image'],
},
],
},
},
},
});
export const { id, displayName, authType, runtime, capabilities } = provider;
```
--------------------------------
### Run Environment Preflight Check
Source: https://github.com/lilimozi/openhanako/blob/main/skills2set/hana-plugin-creator/SKILL.md
Execute the Node.js preflight script to verify the environment before scaffolding a Hana plugin. This script checks for Python 3.10+ and other necessary dependencies.
```bash
node skills2set/hana-plugin-creator/scripts/check_env.mjs --capability scaffold
```
--------------------------------
### Define and Handle Bus Capabilities with Schemas
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGINS_EN.md
Use defineBusHandler for creating capabilities with explicit input/output schemas, permissions, and error definitions. This promotes type safety and clear contracts for inter-plugin communication.
```javascript
import { defineBusHandler, HANA_BUS_SKIP, requestBus } from "@hana/plugin-runtime";
// Plugin A (full-access): register a capability
const bridgeSend = defineBusHandler({
type: "bridge:send",
async handle(payload) {
if (payload.platform !== "telegram") return HANA_BUS_SKIP;
await telegramBot.send(payload.chatId, payload.text);
return { sent: true };
},
});
this.register(this.ctx.bus.handle(
bridgeSend.type,
(payload) => bridgeSend.handle(payload, this.ctx),
{
capability: {
title: "Bridge send",
description: "Send text to a bridge platform.",
inputSchema: {
type: "object",
properties: {
platform: { type: "string" },
chatId: { type: "string" },
text: { type: "string" },
},
required: ["platform", "text"],
},
outputSchema: { type: "object" },
permission: "bridge.send",
errors: ["NO_HANDLER", "TIMEOUT", "INTERNAL_ERROR"],
owner: "plugin:my-plugin",
stability: "experimental",
},
},
));
// Plugin B (any permission): call the capability
const capability = this.ctx.bus.getCapability?.("bridge:send");
if (capability?.available) {
const result = await requestBus(this.ctx, "bridge:send", {
platform: "telegram",
chatId: "123",
text: "Hello",
}, { timeout: 5000 });
}
```
--------------------------------
### Declare Host Capabilities in Manifest
Source: https://github.com/lilimozi/openhanako/blob/main/packages/plugin-sdk/README.md
Declares necessary host capabilities in the 'manifest.json' file for features like opening external links or accessing the clipboard.
```json
{
"manifestVersion": 1,
"ui": {
"hostCapabilities": ["external.open", "clipboard.writeText"]
}
}
```
--------------------------------
### Run Full Validation Suite
Source: https://github.com/lilimozi/openhanako/blob/main/tests/README.md
Execute the complete test suite, type checking, and linting before committing or handing off changes. This ensures all aspects of the project are validated.
```bash
npm test
npm run typecheck
npm run lint
```
--------------------------------
### Execute Baseline Run Command
Source: https://github.com/lilimozi/openhanako/blob/main/skills2set/skill-creator/SKILL.md
Command to execute a task as a baseline, either without a skill or using an older version.
```bash
Baseline run (same prompt, but the baseline depends on context):
- Creating a new skill: no skill at all. Same prompt, no skill path, save to `without_skill/outputs/`.
- Improving an existing skill: the old version. Before editing, snapshot the skill (`cp -r /skill-snapshot/`), then point the baseline subagent at the snapshot. Save to `old_skill/outputs/`.
```
--------------------------------
### Global and Body Styles
Source: https://github.com/lilimozi/openhanako/blob/main/desktop/src/splash.html
Sets up box-sizing, resets margins and padding, defines full height for html and body, and styles the body with a specific font, background, border-radius, flexbox alignment, and a fade-in animation.
```css
HanaAgent *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } html, body { height: 100%; overflow: hidden; } body { font-family: 'Songti SC', 'Georgia', serif; background: #F4F0E4; border-radius: 12px; display: flex; align-items: center; justify-content: center; -webkit-font-smoothing: antialiased; -webkit-app-region: drag; animation: fadeIn 0.3s ease-out; }
```
--------------------------------
### Execute With-Skill Run Command
Source: https://github.com/lilimozi/openhanako/blob/main/skills2set/skill-creator/SKILL.md
Command to execute a task with a specific skill, saving outputs to a designated path.
```bash
Execute this task:
- Skill path:
- Task:
- Input files:
- Save outputs to: /iteration-/eval-/with_skill/outputs/
- Outputs to save:
```
--------------------------------
### Call Text Model for Background Work
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGIN_SDK.md
Use the sampleText utility to call the configured text model for operations like routing, summarization, or RAG query rewriting. Specify the operation, messages, and maxTokens.
```javascript
const { text } = await sampleText(ctx, {
operation: 'world-lore-query',
messages: [{ role: 'user', content: 'Extract search keywords for this scene.' }],
maxTokens: 120,
});
```
--------------------------------
### Import Core Plugin Runtime Functions
Source: https://github.com/lilimozi/openhanako/blob/main/PLUGIN_SDK.md
Import essential functions from the `@hana/plugin-runtime` package for Node-side plugin development. These functions are used for creating agents, sessions, defining plugins and tools, and managing tasks.
```javascript
import {
createAgent,
createSession,
definePlugin,
defineTool,
generateImage,
registerTask,
requestBus,
sampleText,
sendSessionMessage,
} from '@hana/plugin-runtime';
```