mirror of
https://github.com/m5stack/ESP-Claw.git
synced 2026-05-20 11:51:49 -07:00
change(skills): move basic demo skills into main
This commit is contained in:
@@ -0,0 +1 @@
|
||||
fatfs_image/skills
|
||||
@@ -1,80 +0,0 @@
|
||||
# Feishu Messaging
|
||||
|
||||
Use this skill when the user wants to interact through Feishu, especially to reply in the current Feishu conversation or send local files back to Feishu.
|
||||
|
||||
## When to use
|
||||
- The user sends a message from Feishu and expects a reply in the same chat.
|
||||
- The user asks to send a text, image, document, report, log, or other local file to a Feishu chat.
|
||||
- The task is clearly Feishu-specific rather than QQ, Telegram, or WeChat.
|
||||
|
||||
## Available capabilities
|
||||
- `feishu_send_message`: send plain text to a Feishu chat.
|
||||
- `feishu_send_image`: send a local image file to a Feishu chat.
|
||||
- `feishu_send_file`: send a local non-image file to a Feishu chat.
|
||||
|
||||
## Calling rules
|
||||
- Call the direct Feishu capabilities. Do not route Feishu messaging through `cap_cli`.
|
||||
- When replying to the current inbound Feishu conversation, you can omit `chat_id` if the runtime context already contains it.
|
||||
- When starting a new outbound send or the target chat is ambiguous, pass an explicit `chat_id`.
|
||||
- Use `feishu_send_message` for text only.
|
||||
- Use `feishu_send_image` for image files such as `.jpg`, `.jpeg`, `.png`, `.gif`, or `.webp`.
|
||||
- Use `feishu_send_file` for non-image files such as `.txt`, `.json`, `.log`, `.csv`, `.pdf`, or archives.
|
||||
- `caption` is optional for image and file sends. In Feishu media send flow, caption is sent as a follow-up text message.
|
||||
|
||||
## Chat ID guidance
|
||||
- Feishu text send accepts either a chat id or a user `open_id`.
|
||||
- If the target begins with `ou_`, the runtime treats it as a user `open_id`.
|
||||
- Otherwise it is treated as a Feishu `chat_id`.
|
||||
- For replies triggered by an inbound Feishu message, prefer using the current context instead of inventing a `chat_id`.
|
||||
|
||||
## File path guidance
|
||||
- `path` must be a real local filesystem path on the device.
|
||||
- If the exact path is unknown, inspect storage first with file capabilities such as `list_dir`.
|
||||
- Do not pass remote URLs directly to Feishu send capabilities.
|
||||
- In this demo app, inbound Feishu attachments are typically saved under `/fatfs/data/inbox`.
|
||||
|
||||
## Recommended workflow
|
||||
1. Determine whether the user wants text, an image, or a generic file.
|
||||
2. Resolve the target Feishu chat or `open_id`.
|
||||
3. Resolve the local file path if sending media.
|
||||
4. Call `feishu_send_message`, `feishu_send_image`, or `feishu_send_file` directly.
|
||||
5. After the capability returns success, tell the user the reply or file has already been sent.
|
||||
|
||||
## Examples
|
||||
|
||||
Reply with text to the current Feishu chat:
|
||||
```json
|
||||
{
|
||||
"message": "The task has been completed."
|
||||
}
|
||||
```
|
||||
|
||||
Send text to an explicit Feishu user:
|
||||
```json
|
||||
{
|
||||
"chat_id": "ou_xxx123456",
|
||||
"message": "Latest status: device is online."
|
||||
}
|
||||
```
|
||||
|
||||
Send an image:
|
||||
```json
|
||||
{
|
||||
"chat_id": "oc_xxx123456",
|
||||
"path": "/fatfs/data/inbox/capture.jpg",
|
||||
"caption": "Here is the image."
|
||||
}
|
||||
```
|
||||
|
||||
Send a file:
|
||||
```json
|
||||
{
|
||||
"chat_id": "oc_xxx123456",
|
||||
"path": "/fatfs/data/reports/status.json",
|
||||
"caption": "Latest report."
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
- This skill is for Feishu only. If the user is on another IM channel, use that channel's capability group instead.
|
||||
- Feishu send capabilities return JSON such as `{\"ok\":true}` on success.
|
||||
@@ -1,81 +0,0 @@
|
||||
# QQ Messaging
|
||||
|
||||
Use this skill when the user wants to interact through QQ Bot channels, especially to reply in the current QQ conversation or send local files back to QQ.
|
||||
|
||||
## When to use
|
||||
- The user sends a message from QQ and expects a reply in the same chat.
|
||||
- The user asks to send a text, image, screenshot, report, log, or other local file to a QQ chat.
|
||||
- The task is clearly QQ-specific rather than Telegram, Feishu, or WeChat.
|
||||
|
||||
## Available capabilities
|
||||
- `qq_send_message`: send plain text to a QQ chat.
|
||||
- `qq_send_image`: send a local image file to a QQ chat.
|
||||
- `qq_send_file`: send a local non-image file to a QQ chat.
|
||||
|
||||
## Calling rules
|
||||
- Call the direct QQ capabilities. Do not route QQ messaging through `cap_cli`.
|
||||
- When replying to the current inbound QQ conversation, you can omit `chat_id` if the runtime context already contains it.
|
||||
- When starting a new outbound send or the target chat is ambiguous, pass an explicit `chat_id`.
|
||||
- Use `qq_send_message` for text only.
|
||||
- Use `qq_send_image` for image files such as `.jpg`, `.jpeg`, `.png`, `.gif`, or `.webp`.
|
||||
- Use `qq_send_file` for non-image files such as `.txt`, `.json`, `.log`, `.csv`, `.pdf`, or archives.
|
||||
- `caption` is optional for image and file sends. Include it only when the user wants accompanying text.
|
||||
|
||||
## Chat ID guidance
|
||||
- Private QQ chats are normalized as `c2c:<openid>`.
|
||||
- Group QQ chats are normalized as `group:<group_openid>`.
|
||||
- For replies triggered by an inbound QQ message, prefer using the current context instead of inventing a `chat_id`.
|
||||
- If the user explicitly provides a QQ target, preserve it exactly when it already matches the expected `c2c:` or `group:` format.
|
||||
|
||||
## File path guidance
|
||||
- `path` must be a real local filesystem path on the device.
|
||||
- If the exact path is unknown, inspect storage first with file capabilities such as `list_dir`.
|
||||
- Do not attempt to send remote URLs directly. Download or locate the file on local storage first.
|
||||
- In this demo app, inbound QQ attachments are typically saved under `/fatfs/data/inbox`.
|
||||
|
||||
## Recommended workflow
|
||||
1. Determine whether the user wants text, an image, or a generic file.
|
||||
2. Resolve the target chat.
|
||||
3. Resolve the local file path if sending media.
|
||||
4. Call `qq_send_message`, `qq_send_image`, or `qq_send_file` directly.
|
||||
5. After the capability returns success, tell the user the reply or file has already been sent.
|
||||
|
||||
## Examples
|
||||
|
||||
Reply with text to the current QQ chat:
|
||||
```json
|
||||
{
|
||||
"message": "The task has been completed."
|
||||
}
|
||||
```
|
||||
|
||||
Send text to an explicit QQ group:
|
||||
```json
|
||||
{
|
||||
"chat_id": "c2c:1234567890",
|
||||
"message": "Latest status: device is online."
|
||||
}
|
||||
```
|
||||
|
||||
Send an image:
|
||||
```json
|
||||
{
|
||||
"chat_id": "c2c:1234567890",
|
||||
"path": "/fatfs/data/inbox/capture.jpg",
|
||||
"caption": "Here is the image."
|
||||
}
|
||||
```
|
||||
|
||||
Send a file:
|
||||
```json
|
||||
{
|
||||
"chat_id": "c2c:abcdefg123456",
|
||||
"path": "/fatfs/data/reports/status.json",
|
||||
"caption": "Latest report."
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
- This skill is for QQ only. If the user is on another IM channel
|
||||
- The capability returns success text like `reply already sent to user`; do not repeat the same content again as if it still needs to be delivered.
|
||||
- Generic file delivery may still depend on QQ platform-side support. If `qq_send_file` fails, report the failure clearly and consider whether the file can be sent as an image instead.
|
||||
@@ -1,79 +0,0 @@
|
||||
# Telegram Messaging
|
||||
|
||||
Use this skill when the user wants to interact through Telegram, especially to reply in the current Telegram conversation or send local files back to Telegram.
|
||||
|
||||
## When to use
|
||||
- The user sends a message from Telegram and expects a reply in the same chat.
|
||||
- The user asks to send a text, image, screenshot, report, log, or other local file to a Telegram chat.
|
||||
- The task is clearly Telegram-specific rather than QQ, Feishu, or WeChat.
|
||||
|
||||
## Available capabilities
|
||||
- `tg_send_message`: send plain text to a Telegram chat.
|
||||
- `tg_send_image`: send a local image file to a Telegram chat.
|
||||
- `tg_send_file`: send a local non-image file to a Telegram chat.
|
||||
|
||||
## Calling rules
|
||||
- Call the direct Telegram capabilities. Do not route Telegram messaging through `cap_cli`.
|
||||
- When replying to the current inbound Telegram conversation, you can omit `chat_id` if the runtime context already contains it.
|
||||
- When starting a new outbound send or the target chat is ambiguous, pass an explicit `chat_id`.
|
||||
- Use `tg_send_message` for text only.
|
||||
- Use `tg_send_image` for image files such as `.jpg`, `.jpeg`, `.png`, `.gif`, or `.webp`.
|
||||
- Use `tg_send_file` for non-image files such as `.txt`, `.json`, `.log`, `.csv`, `.pdf`, or archives.
|
||||
- `caption` is optional for image and file sends. Include it only when the user wants accompanying text.
|
||||
|
||||
## Chat ID guidance
|
||||
- Telegram `chat_id` is usually a numeric string such as `"123456789"` or `"-1001234567890"`.
|
||||
- For replies triggered by an inbound Telegram message, prefer using the current context instead of reconstructing the `chat_id`.
|
||||
- If the user gives a Telegram target explicitly, preserve it exactly.
|
||||
|
||||
## File path guidance
|
||||
- `path` must be a real local filesystem path on the device.
|
||||
- If the exact path is unknown, inspect storage first with file capabilities such as `list_dir`.
|
||||
- Do not pass remote URLs directly to Telegram send capabilities.
|
||||
- In this demo app, inbound Telegram attachments are typically saved under `/fatfs/data/inbox`.
|
||||
|
||||
## Recommended workflow
|
||||
1. Determine whether the user wants text, an image, or a generic file.
|
||||
2. Resolve the target Telegram chat.
|
||||
3. Resolve the local file path if sending media.
|
||||
4. Call `tg_send_message`, `tg_send_image`, or `tg_send_file` directly.
|
||||
5. After the capability returns success, tell the user the reply or file has already been sent.
|
||||
|
||||
## Examples
|
||||
|
||||
Reply with text to the current Telegram chat:
|
||||
```json
|
||||
{
|
||||
"message": "Task completed."
|
||||
}
|
||||
```
|
||||
|
||||
Send text to an explicit Telegram chat:
|
||||
```json
|
||||
{
|
||||
"chat_id": "-1001234567890",
|
||||
"message": "Latest status: device is online."
|
||||
}
|
||||
```
|
||||
|
||||
Send an image:
|
||||
```json
|
||||
{
|
||||
"chat_id": "123456789",
|
||||
"path": "/fatfs/data/inbox/capture.jpg",
|
||||
"caption": "Here is the image."
|
||||
}
|
||||
```
|
||||
|
||||
Send a file:
|
||||
```json
|
||||
{
|
||||
"chat_id": "123456789",
|
||||
"path": "/fatfs/data/reports/status.json",
|
||||
"caption": "Latest report."
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
- This skill is for Telegram only. If the user is on another IM channel, use that channel's capability group instead.
|
||||
- The capability returns success text like `reply already sent to user`; do not phrase the result as a pending action.
|
||||
@@ -1,63 +0,0 @@
|
||||
# WeChat Messaging
|
||||
|
||||
Use this skill when the user wants to interact through WeChat, especially to send text or a local image to a WeChat contact or group.
|
||||
|
||||
## When to use
|
||||
- The user asks to send a text reply or image through WeChat.
|
||||
- The task is clearly WeChat-specific rather than QQ, Telegram, or Feishu.
|
||||
- The target WeChat `chat_id` is known or can be taken from the current workflow context outside the capability call.
|
||||
|
||||
## Available capabilities
|
||||
- `wechat_send_message`: send plain text to a WeChat chat.
|
||||
- `wechat_send_image`: send a local image file to a WeChat chat.
|
||||
- `wechat_gateway`: WeChat inbound event source. This is infrastructure, not a capability the model should call directly.
|
||||
|
||||
## Calling rules
|
||||
- Call the direct WeChat capabilities. Do not route WeChat messaging through `cap_cli`.
|
||||
- `wechat_send_message` requires explicit `chat_id` and `message`.
|
||||
- `wechat_send_image` requires explicit `chat_id` and `path`, with optional `caption`.
|
||||
- Unlike Telegram, QQ, and Feishu, the current WeChat callable implementation does not fall back to `ctx->chat_id`. Always pass `chat_id` explicitly.
|
||||
- Use `wechat_send_message` for text only.
|
||||
- Use `wechat_send_image` for image files such as `.jpg`, `.jpeg`, `.png`, `.gif`, or `.webp`.
|
||||
|
||||
## Chat ID guidance
|
||||
- WeChat inbound routing uses the group id when present, otherwise the sender user id.
|
||||
- In practice, `chat_id` is typically a room id or a user id string already known by the integration.
|
||||
- If the user gives a concrete WeChat target such as a room id or contact id, preserve it exactly.
|
||||
|
||||
## File path guidance
|
||||
- `path` must be a real local filesystem path on the device.
|
||||
- If the exact image path is unknown, inspect storage first with file capabilities such as `list_dir`.
|
||||
- Do not pass remote URLs directly to WeChat send capabilities.
|
||||
- In this demo app, inbound WeChat media is typically saved under `/fatfs/data/inbox`.
|
||||
|
||||
## Recommended workflow
|
||||
1. Confirm that the target channel is WeChat.
|
||||
2. Resolve the explicit WeChat `chat_id`.
|
||||
3. Resolve the local image path if sending media.
|
||||
4. Call `wechat_send_message` or `wechat_send_image` directly.
|
||||
5. After the capability returns success, tell the user the message or image has already been sent.
|
||||
|
||||
## Examples
|
||||
|
||||
Send text to a WeChat chat:
|
||||
```json
|
||||
{
|
||||
"chat_id": "room123",
|
||||
"message": "Latest status: device is online."
|
||||
}
|
||||
```
|
||||
|
||||
Send an image:
|
||||
```json
|
||||
{
|
||||
"chat_id": "wxid_abc123",
|
||||
"path": "/fatfs/data/inbox/capture.jpg",
|
||||
"caption": "Here is the image."
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
- This skill is for WeChat only. If the user is on another IM channel, use that channel's capability group instead.
|
||||
- The current capability surface supports text and image send, but not generic non-image file send.
|
||||
- WeChat send capabilities return `{\"ok\":true}` on success.
|
||||
@@ -1,35 +0,0 @@
|
||||
# Image Inspection
|
||||
|
||||
Use this skill when the user wants the device to inspect a local image and describe what is visible.
|
||||
|
||||
## When to use
|
||||
- The user asks what is in an image, photo, screenshot, or camera frame.
|
||||
- The image already exists on the device filesystem.
|
||||
- The task needs visual analysis rather than plain file reading.
|
||||
|
||||
## Available capability
|
||||
- `inspect_image`: analyze one local image from an absolute path using a prompt that says what to inspect.
|
||||
|
||||
## Calling rules
|
||||
- Call `inspect_image` directly.
|
||||
- Always pass an absolute local file path in `path`.
|
||||
- Always pass a clear `prompt` that tells the model what to look for.
|
||||
- Confirm or discover the image path first if it is not already known.
|
||||
- Do not pass remote URLs or non-image files.
|
||||
|
||||
## Path guidance
|
||||
- Prefer real local paths already stored on the device.
|
||||
- If the exact path is unknown, inspect storage first with file capabilities such as `list_dir`.
|
||||
- Common roots in this demo include `/fatfs/data/inbox`, `/fatfs/data`, or other application-managed storage paths.
|
||||
|
||||
## Example
|
||||
```json
|
||||
{
|
||||
"path": "/fatfs/data/inbox/photo.jpg",
|
||||
"prompt": "Describe the main objects in this image and mention any visible text."
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
- Keep the prompt specific. For example: identify objects, read visible text, describe a scene, or check whether a target item appears.
|
||||
- If the image is blurry or uncertain, report that uncertainty instead of over-claiming.
|
||||
@@ -1,69 +0,0 @@
|
||||
# Lua Script Execution
|
||||
|
||||
Use this skill when the user wants to see existing Lua scripts, run one, or inspect async execution jobs.
|
||||
|
||||
## Command Rule
|
||||
- The LLM should call Lua through the direct capability execute entrypoints, not through `cap_cli`.
|
||||
- Use `lua_list_scripts` to inspect scripts.
|
||||
- Use `lua_run_script` for synchronous execution.
|
||||
- Use `lua_run_script_async` for long-running or continuous scripts.
|
||||
- Use `lua_list_async_jobs` and `lua_get_async_job` to inspect async jobs.
|
||||
|
||||
## Running a Script Synchronously
|
||||
Use `lua_run_script` when the user wants immediate output.
|
||||
- Required: `path`
|
||||
- Optional: `args`, `timeout_ms`
|
||||
- Prefer relative paths such as `hello.lua`
|
||||
|
||||
Examples:
|
||||
```json
|
||||
{
|
||||
"path": "hello.lua"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"path": "blink.lua",
|
||||
"args": {
|
||||
"pin": 2
|
||||
},
|
||||
"timeout_ms": 3000
|
||||
}
|
||||
```
|
||||
|
||||
If the script expects structured inputs, pass them through `args`. The runtime exposes them to Lua as the global `args`.
|
||||
|
||||
## Running a Script Asynchronously
|
||||
Use `lua_run_script_async` for long-running or continuous scripts.
|
||||
- Required: `path`
|
||||
- Optional: `args`, `timeout_ms`
|
||||
|
||||
Examples:
|
||||
```json
|
||||
{
|
||||
"path": "blink.lua"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"path": "blink.lua",
|
||||
"args": {
|
||||
"pin": 2
|
||||
},
|
||||
"timeout_ms": 3000
|
||||
}
|
||||
```
|
||||
|
||||
After starting an async script:
|
||||
- Use `lua_list_async_jobs`
|
||||
- Use `lua_list_async_jobs` with `{"status":"running"}`
|
||||
- Use `lua_get_async_job` with `{"job_id":"<job_id>"}`
|
||||
|
||||
## Execution Notes
|
||||
- Paths must resolve under `/spiffs/lua` and end with `.lua`.
|
||||
- `--timeout-ms` must be a positive integer when provided.
|
||||
- Prefer synchronous run for short scripts that should finish and return text.
|
||||
- Prefer async run for loops, animations, watchers, or long-running device behaviors.
|
||||
- If the user asks to run a script that does not exist yet, switch to the Lua authoring flow first.
|
||||
@@ -1,14 +0,0 @@
|
||||
# Lua Delay
|
||||
|
||||
This skill describes how to correctly use delay when writing Lua scripts.
|
||||
|
||||
## How to call
|
||||
- Import it with `local delay = require("delay")`
|
||||
- Call `delay.delay_ms(ms)` to sleep for a number of milliseconds
|
||||
- `ms` should be an integer greater than or equal to `0`
|
||||
|
||||
## Example
|
||||
```lua
|
||||
local delay = require("delay")
|
||||
delay.delay_ms(500)
|
||||
```
|
||||
@@ -1,21 +0,0 @@
|
||||
# Lua Event Publisher
|
||||
|
||||
This skill describes how to correctly use event_publisher when writing Lua scripts.
|
||||
|
||||
## How to call
|
||||
- Import it with `local event_publisher = require("event_publisher")`
|
||||
- Call `event_publisher.publish_message({...})` to publish a message event
|
||||
- Call `event_publisher.publish_trigger({...})` to publish a trigger event
|
||||
- Call `event_publisher.publish({...})` to publish a full custom event
|
||||
|
||||
## Example
|
||||
```lua
|
||||
local event_publisher = require("event_publisher")
|
||||
|
||||
event_publisher.publish_message({
|
||||
source_cap = "lua_script",
|
||||
channel = "custom",
|
||||
chat_id = "demo",
|
||||
text = "hello"
|
||||
})
|
||||
```
|
||||
@@ -1,16 +0,0 @@
|
||||
# Lua GPIO
|
||||
|
||||
This skill describes how to correctly use gpio when writing Lua scripts.
|
||||
|
||||
## How to call
|
||||
- Import it with `local gpio = require("gpio")`
|
||||
- Call `gpio.set_direction(pin, mode)` to set pin mode
|
||||
- Call `gpio.set_level(pin, level)` to set output level
|
||||
- Call `gpio.get_level(pin)` to read pin level
|
||||
|
||||
## Example
|
||||
```lua
|
||||
local gpio = require("gpio")
|
||||
gpio.set_direction(2, "output")
|
||||
gpio.set_level(2, 1)
|
||||
```
|
||||
@@ -1,19 +0,0 @@
|
||||
# Lua LED Strip
|
||||
|
||||
This skill describes how to correctly use led_strip when writing Lua scripts.
|
||||
|
||||
## How to call
|
||||
- Import it with `local led_strip = require("led_strip")`
|
||||
- Call `local strip = led_strip.new(gpio, max_leds)` to create a strip handle
|
||||
- Call `strip:set_pixel(index, r, g, b)` to set one pixel
|
||||
- Call `strip:refresh()` to apply changes
|
||||
- Call `strip:clear()` or `strip:close()` when needed
|
||||
|
||||
## Example
|
||||
```lua
|
||||
local led_strip = require("led_strip")
|
||||
|
||||
local strip = led_strip.new(8, 1)
|
||||
strip:set_pixel(0, 255, 0, 0)
|
||||
strip:refresh()
|
||||
```
|
||||
@@ -1,18 +0,0 @@
|
||||
# Lua Storage
|
||||
|
||||
This skill describes how to correctly use storage when writing Lua scripts.
|
||||
|
||||
## How to call
|
||||
- Import it with `local storage = require("storage")`
|
||||
- Call `storage.mkdir(path)` to create a directory
|
||||
- Call `storage.write_file(path, content)` to write a file
|
||||
- Call `storage.read_file(path)` to read a file
|
||||
|
||||
## Example
|
||||
```lua
|
||||
local storage = require("storage")
|
||||
|
||||
storage.mkdir("/fatfs/data/demo")
|
||||
storage.write_file("/fatfs/data/demo/test.txt", "hello")
|
||||
local text = storage.read_file("/fatfs/data/demo/test.txt")
|
||||
```
|
||||
@@ -1,69 +0,0 @@
|
||||
# Lua Script Execution
|
||||
|
||||
Use this skill when the user wants to see existing Lua scripts, run one, or inspect async execution jobs.
|
||||
|
||||
## Command Rule
|
||||
- The LLM should call Lua through the direct capability execute entrypoints.
|
||||
- Use `lua_list_scripts` to inspect scripts.
|
||||
- Use `lua_run_script` for synchronous execution.
|
||||
- Use `lua_run_script_async` for long-running or continuous scripts.
|
||||
- Use `lua_list_async_jobs` and `lua_get_async_job` to inspect async jobs.
|
||||
|
||||
## Running a Script Synchronously
|
||||
Use `lua_run_script` when the user wants immediate output.
|
||||
- Required: `path`
|
||||
- Optional: `args`, `timeout_ms`
|
||||
- Prefer relative paths such as `hello.lua`
|
||||
|
||||
Examples:
|
||||
```json
|
||||
{
|
||||
"path": "hello.lua"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"path": "blink.lua",
|
||||
"args": {
|
||||
"pin": 2
|
||||
},
|
||||
"timeout_ms": 3000
|
||||
}
|
||||
```
|
||||
|
||||
If the script expects structured inputs, pass them through `args`. The runtime exposes them to Lua as the global `args`.
|
||||
|
||||
## Running a Script Asynchronously
|
||||
Use `lua_run_script_async` for long-running or continuous scripts.
|
||||
- Required: `path`
|
||||
- Optional: `args`, `timeout_ms`
|
||||
|
||||
Examples:
|
||||
```json
|
||||
{
|
||||
"path": "blink.lua"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"path": "blink.lua",
|
||||
"args": {
|
||||
"pin": 2
|
||||
},
|
||||
"timeout_ms": 3000
|
||||
}
|
||||
```
|
||||
|
||||
After starting an async script:
|
||||
- Use `lua_list_async_jobs`
|
||||
- Use `lua_list_async_jobs` with `{"status":"running"}`
|
||||
- Use `lua_get_async_job` with `{"job_id":"<job_id>"}`
|
||||
|
||||
## Execution Notes
|
||||
- Paths must resolve under `/spiffs/lua` and end with `.lua`.
|
||||
- `--timeout-ms` must be a positive integer when provided.
|
||||
- Prefer synchronous run for short scripts that should finish and return text.
|
||||
- Prefer async run for loops, animations, watchers, or long-running device behaviors.
|
||||
- If the user asks to run a script that does not exist yet, switch to the Lua authoring flow first.
|
||||
@@ -1,64 +0,0 @@
|
||||
# Lua Script Authoring
|
||||
|
||||
Use this skill when the user wants to write, generate, or modify a managed Lua script for this device.
|
||||
|
||||
## Runtime Constraints
|
||||
- Managed Lua scripts must stay under `/spiffs/lua`.
|
||||
- The script path must end with `.lua`.
|
||||
- Prefer relative paths such as `blink.lua` or `rainbow.lua`; the runtime resolves them under `/spiffs/lua`.
|
||||
- Use `lua_write_script` to save or overwrite script content.
|
||||
|
||||
## Available Lua Modules
|
||||
The runtime includes these built-in and application-registered modules:
|
||||
|
||||
### `delay`
|
||||
- `delay.delay_ms(ms)`
|
||||
- Use for short blocking delays inside a script.
|
||||
|
||||
### `storage`
|
||||
- `storage.mkdir(path)`
|
||||
- `storage.write_file(path, content)`
|
||||
- `storage.read_file(path)`
|
||||
- Use only for files the script needs to manage.
|
||||
|
||||
### `gpio`
|
||||
- `gpio.set_direction(pin, mode)`
|
||||
- `gpio.set_level(pin, level)`
|
||||
- `gpio.get_level(pin)`
|
||||
- Supported modes: `input`, `output`, `input_output`, `output_od`, `input_output_od`, `disable`
|
||||
|
||||
### `led_strip`
|
||||
- `local strip = led_strip.new(gpio_pin, max_leds)`
|
||||
- `strip:set_pixel(index, r, g, b)`
|
||||
- `strip:refresh()`
|
||||
- `strip:clear()`
|
||||
- `strip:close()`
|
||||
- This is for WS2812-style LED strips on a GPIO pin.
|
||||
|
||||
## Writing Guidance
|
||||
- Write plain Lua script files, not markdown or pseudocode.
|
||||
- Keep dependencies limited to standard Lua plus the modules listed above.
|
||||
- Prefer small scripts with a clear entry flow and explicit comments for pin usage.
|
||||
- If the script touches GPIO or LED hardware, state the pin numbers and expected electrical behavior in comments.
|
||||
- If a requested peripheral is not covered by `gpio` or `led_strip`, say that the current runtime does not expose that peripheral module.
|
||||
|
||||
## Example Shape
|
||||
```lua
|
||||
local gpio = require("gpio")
|
||||
local delay = require("delay")
|
||||
|
||||
gpio.set_direction(2, "output")
|
||||
|
||||
while true do
|
||||
gpio.set_level(2, 1)
|
||||
delay.delay_ms(500)
|
||||
gpio.set_level(2, 0)
|
||||
delay.delay_ms(500)
|
||||
end
|
||||
```
|
||||
|
||||
## Save Rule
|
||||
When the script is ready, call `lua_write_script` with:
|
||||
- `path`: relative `.lua` path under `/spiffs/lua`
|
||||
- `content`: full Lua source
|
||||
- `overwrite`: `true` only when replacing an existing script intentionally
|
||||
@@ -1,55 +0,0 @@
|
||||
# QQ File Return
|
||||
|
||||
Use this skill when the user wants the device to send a local file or image back to a QQ chat.
|
||||
|
||||
## When to use
|
||||
- The user asks to send back a file, attachment, image, photo, log, or generated output through QQ.
|
||||
- The target conversation is already the active QQ chat, or the user provides an explicit QQ `chat_id`.
|
||||
|
||||
## Available tools
|
||||
- `list_dir`: inspect device storage and confirm the file path
|
||||
- `read_file`: inspect small text files before sending when needed
|
||||
- Direct QQ capabilities: `qq_send_image` and `qq_send_file`
|
||||
|
||||
## Path guidance
|
||||
- Prefer real local paths already stored on the device.
|
||||
- Common roots in this demo are `/spiffs`, `/spiffs/qq`, `/spiffs/lua`, or other application-managed storage paths.
|
||||
- Use `list_dir` first if the exact file path is unknown.
|
||||
- Use `read_file` only for small text inspection, not for binary payloads.
|
||||
|
||||
## Sending rules
|
||||
- Use `qq_send_image` for image files such as `.jpg`, `.jpeg`, `.png`, `.gif`, or `.webp`.
|
||||
- Use `qq_send_file` for non-image files such as `.txt`, `.json`, `.log`, `.csv`, or archives.
|
||||
- Execute the chosen QQ capability directly.
|
||||
- Pass `caption` only when the user wants an accompanying message.
|
||||
- The JSON payload should include an explicit QQ `chat_id`, `path`, and optional `caption`.
|
||||
|
||||
## Examples
|
||||
|
||||
Send an image to a QQ chat:
|
||||
```json
|
||||
{
|
||||
"chat_id": "group123",
|
||||
"path": "/spiffs/qq/capture.jpg",
|
||||
"caption": "Here is the image."
|
||||
}
|
||||
```
|
||||
|
||||
Send a file to a QQ group:
|
||||
```json
|
||||
{
|
||||
"chat_id": "group1234567890",
|
||||
"path": "/spiffs/reports/status.json",
|
||||
"caption": "Latest status report."
|
||||
}
|
||||
```
|
||||
|
||||
## Workflow
|
||||
1. Confirm the target file exists with `list_dir` if needed.
|
||||
2. Choose `qq_send_image` or `qq_send_file` based on file type.
|
||||
3. Execute the QQ capability directly with `chat_id`, `path`, and optional `caption`.
|
||||
4. Tell the user whether the send succeeded.
|
||||
|
||||
## Notes
|
||||
- This skill only sends files that already exist on the device filesystem.
|
||||
- QQ generic file delivery may depend on platform-side enablement. If `qq_send_file` fails, prefer falling back to `qq_send_image` for images or explain that QQ rejected generic file upload.
|
||||
@@ -1,103 +0,0 @@
|
||||
{
|
||||
"skills": [
|
||||
{
|
||||
"id": "cap_im_feishu",
|
||||
"file": "cap_im_feishu.md",
|
||||
"title": "cap_im_feishu",
|
||||
"summary": "How to reply and send text, images, and files through Feishu capabilities.",
|
||||
"cap_groups": [
|
||||
"cap_im_feishu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cap_im_qq",
|
||||
"file": "cap_im_qq.md",
|
||||
"title": "cap_im_qq",
|
||||
"summary": "Reply in the QQ conversation or send local files back to QQ",
|
||||
"cap_groups": [
|
||||
"cap_im_qq"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cap_im_tg",
|
||||
"file": "cap_im_tg.md",
|
||||
"title": "cap_im_tg",
|
||||
"summary": "How to reply and send text, images, and files through Telegram capabilities.",
|
||||
"cap_groups": [
|
||||
"cap_im_tg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cap_im_wechat",
|
||||
"file": "cap_im_wechat.md",
|
||||
"title": "cap_im_wechat",
|
||||
"summary": "How to send text and images through WeChat capabilities.",
|
||||
"cap_groups": [
|
||||
"cap_im_wechat"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cap_llm_inspect_image",
|
||||
"file": "cap_llm_inspect.md",
|
||||
"title": "cap_llm_inspect_image",
|
||||
"summary": "How to inspect a local image with inspect_image.",
|
||||
"cap_groups": [
|
||||
"cap_llm_inspect"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cap_lua_run",
|
||||
"file": "cap_lua_run.md",
|
||||
"title": "cap_lua_run",
|
||||
"summary": "This Files describes how to correctly use Lua scripts.",
|
||||
"cap_groups": [
|
||||
"cap_lua"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "lua_module_delay",
|
||||
"file": "lua_module_delay.md",
|
||||
"title": "lua_module_delay",
|
||||
"summary": "How to call delay.delay_ms(ms) from Lua after loading the delay module.",
|
||||
"cap_groups": [
|
||||
"cap_lua"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "lua_module_event_publisher",
|
||||
"file": "lua_module_event_publisher.md",
|
||||
"title": "lua_module_event_publisher",
|
||||
"summary": "How to publish message, trigger, and custom events from Lua.",
|
||||
"cap_groups": [
|
||||
"cap_lua"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "lua_module_gpio",
|
||||
"file": "lua_module_gpio.md",
|
||||
"title": "lua_module_gpio",
|
||||
"summary": "How to set GPIO direction, write level, and read level from Lua.",
|
||||
"cap_groups": [
|
||||
"cap_lua"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "lua_module_led_strip",
|
||||
"file": "lua_module_led_strip.md",
|
||||
"title": "lua_module_led_strip",
|
||||
"summary": "How to create and control an LED strip from Lua.",
|
||||
"cap_groups": [
|
||||
"cap_lua"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "lua_module_storage",
|
||||
"file": "lua_module_storage.md",
|
||||
"title": "lua_module_storage",
|
||||
"summary": "How to create directories and read or write files from Lua.",
|
||||
"cap_groups": [
|
||||
"cap_lua"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"skills": [
|
||||
{
|
||||
"id": "weather_search",
|
||||
"file": "weather.md",
|
||||
"title": "weather",
|
||||
"summary": "How to answer current weather, temperature, and forecast queries through direct web search capabilities.",
|
||||
"cap_groups": [
|
||||
"web_search"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -43,9 +43,9 @@ def load_json_file(path: Path) -> object:
|
||||
try:
|
||||
return json.loads(path.read_text(encoding='utf-8'))
|
||||
except FileNotFoundError:
|
||||
fail(f"Missing JSON file: {path}")
|
||||
fail(f'Missing JSON file: {path}')
|
||||
except json.JSONDecodeError as exc:
|
||||
fail(f"Invalid JSON in {path}: {exc}")
|
||||
fail(f'Invalid JSON in {path}: {exc}')
|
||||
|
||||
|
||||
def load_manifest(path: Path) -> dict:
|
||||
@@ -54,7 +54,7 @@ def load_manifest(path: Path) -> dict:
|
||||
|
||||
data = load_json_file(path)
|
||||
if not isinstance(data, dict):
|
||||
fail(f"Manifest must be a JSON object: {path}")
|
||||
fail(f'Manifest must be a JSON object: {path}')
|
||||
return {
|
||||
'component_entries': list(data.get('component_entries', [])),
|
||||
'component_files': list(data.get('component_files', [])),
|
||||
@@ -109,9 +109,12 @@ def recover_demo_entries(
|
||||
|
||||
|
||||
def load_demo_entries(skills_list_path: Path, manifest: dict, component_entries: list[dict]) -> list[dict]:
|
||||
if not skills_list_path.exists():
|
||||
return []
|
||||
|
||||
data = load_json_file(skills_list_path)
|
||||
if not isinstance(data, dict):
|
||||
fail(f"{skills_list_path} must be a JSON object.")
|
||||
fail(f'{skills_list_path} must be a JSON object.')
|
||||
|
||||
skills = data.get('skills')
|
||||
if not isinstance(skills, list):
|
||||
@@ -140,7 +143,7 @@ def collect_component_skills(
|
||||
|
||||
data = load_json_file(json_path)
|
||||
if not isinstance(data, dict):
|
||||
fail(f"{json_path} must be a JSON object.")
|
||||
fail(f'{json_path} must be a JSON object.')
|
||||
|
||||
skills = data.get('skills')
|
||||
if not isinstance(skills, list):
|
||||
@@ -161,17 +164,17 @@ def collect_component_skills(
|
||||
if skill_path.parent != skills_dir.resolve():
|
||||
fail(
|
||||
f"Component '{component_name}' declares skill file outside its skills directory: "
|
||||
f"{skill_file} ({json_path})"
|
||||
f'{skill_file} ({json_path})'
|
||||
)
|
||||
if not skill_path.is_file():
|
||||
fail(
|
||||
f"Component '{component_name}' references missing skill file '{skill_file}' "
|
||||
f"in {json_path}"
|
||||
f'in {json_path}'
|
||||
)
|
||||
if skill_path.suffix.lower() != '.md':
|
||||
fail(
|
||||
f"Component '{component_name}' declares non-markdown skill file '{skill_file}' "
|
||||
f"in {json_path}"
|
||||
f'in {json_path}'
|
||||
)
|
||||
|
||||
previous_id = skill_id_sources.get(skill_id)
|
||||
@@ -233,7 +236,7 @@ def merge_entries(
|
||||
|
||||
for entry in demo_entries:
|
||||
skill_file = entry.get('file')
|
||||
register(entry, f"demo skills list ({demo_skills_dir / skill_file})")
|
||||
register(entry, f'demo skills list ({demo_skills_dir / skill_file})')
|
||||
|
||||
for entry in component_entries:
|
||||
entry_key = (str(entry.get('id')), str(entry.get('file')))
|
||||
@@ -249,7 +252,7 @@ def validate_demo_markdown_files(demo_entries: list[dict], demo_skills_dir: Path
|
||||
if not skill_path.is_file():
|
||||
fail(
|
||||
f"Demo skills_list.json references missing skill file '{skill_file}' "
|
||||
f"at {skill_path}"
|
||||
f'at {skill_path}'
|
||||
)
|
||||
|
||||
|
||||
@@ -261,6 +264,8 @@ def sync_markdown_files(
|
||||
merged_entries: list[dict],
|
||||
component_entries: list[dict],
|
||||
) -> None:
|
||||
demo_skills_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for old_file in manifest.get('component_files', []):
|
||||
if old_file not in copy_map:
|
||||
stale_path = demo_skills_dir / old_file
|
||||
@@ -290,8 +295,6 @@ def sync_markdown_files(
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
demo_skills_dir = Path(args.demo_skills_dir).resolve()
|
||||
if not demo_skills_dir.is_dir():
|
||||
fail(f"Demo skills directory does not exist: {demo_skills_dir}")
|
||||
|
||||
skills_list_path = demo_skills_dir / SKILLS_LIST_FILE
|
||||
manifest_path = Path(args.manifest_path).resolve()
|
||||
@@ -324,5 +327,5 @@ if __name__ == '__main__':
|
||||
try:
|
||||
sys.exit(main())
|
||||
except SkillSyncError as exc:
|
||||
print(f"sync_component_skills.py: error: {exc}", file=sys.stderr)
|
||||
print(f'sync_component_skills.py: error: {exc}', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
Reference in New Issue
Block a user