refactor: simplify skill catalog metadata

This commit is contained in:
loop
2026-04-18 20:35:55 +08:00
parent 5a2c89591a
commit ddd3878736
14 changed files with 23 additions and 65 deletions
@@ -27,28 +27,6 @@
}
]
},
{
"id": "im_hello_esp_claw",
"description": "Intercept IM hello! ESP-Claw and reply directly.Just for demonstration",
"enabled": true,
"ack": "{{event.source_channel}} hello intercepted",
"match": {
"event_type": "message",
"event_key": "text",
"content_type": "text",
"text": "hello! ESP-Claw"
},
"actions": [
{
"type": "send_message",
"input": {
"channel": "{{event.source_channel}}",
"chat_id": "{{event.chat_id}}",
"message": "Nice to meet you!"
}
}
]
},
{
"id": "im_any_message_working_reply",
"description": "Reply to any IM text message before other automation handling.",
@@ -3,7 +3,6 @@
{
"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": [
"cap_time",
@@ -3,7 +3,6 @@
{
"id": "cap_im_feishu",
"file": "cap_im_feishu.md",
"title": "cap_im_feishu",
"summary": "How to send additional text, images, and files to Feishu.",
"cap_groups": [
"cap_im_feishu"
@@ -3,7 +3,6 @@
{
"id": "cap_im_qq",
"file": "cap_im_qq.md",
"title": "cap_im_qq",
"summary": "How to send additional text or send local files back to QQ.",
"cap_groups": [
"cap_im_qq"
@@ -3,7 +3,6 @@
{
"id": "cap_im_tg",
"file": "cap_im_tg.md",
"title": "cap_im_tg",
"summary": "How to send additional text, images, and files to Telegram.",
"cap_groups": [
"cap_im_tg"
@@ -3,7 +3,6 @@
{
"id": "cap_im_wechat",
"file": "cap_im_wechat.md",
"title": "cap_im_wechat",
"summary": "How to send additional text and images to WeChat.",
"cap_groups": [
"cap_im_wechat"
@@ -3,7 +3,6 @@
{
"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"
@@ -3,7 +3,6 @@
{
"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"
@@ -3,7 +3,6 @@
{
"id": "cap_router_mgr",
"file": "cap_router_mgr.md",
"title": "cap_router_mgr",
"summary": "Manage router automation rules: list/get/add/update/delete/reload with strict rule_json format.",
"cap_groups": [
"cap_router_mgr"
@@ -3,7 +3,6 @@
{
"id": "cap_scheduler",
"file": "cap_scheduler.md",
"title": "cap_scheduler",
"summary": "Manage scheduler rules: list/get/add/enable/disable/pause/resume/trigger/reload.",
"cap_groups": [
"cap_scheduler"
@@ -558,7 +558,6 @@ static esp_err_t cap_skill_register_execute(const char *input_json,
cJSON *skills = NULL;
cJSON *skill_id_item = NULL;
cJSON *file_item = NULL;
cJSON *title_item = NULL;
cJSON *summary_item = NULL;
cJSON *skill = NULL;
esp_err_t err;
@@ -573,14 +572,12 @@ static esp_err_t cap_skill_register_execute(const char *input_json,
skill_id_item = cJSON_GetObjectItemCaseSensitive(root, "skill_id");
file_item = cJSON_GetObjectItemCaseSensitive(root, "file");
title_item = cJSON_GetObjectItemCaseSensitive(root, "title");
summary_item = cJSON_GetObjectItemCaseSensitive(root, "summary");
if (!cJSON_IsString(skill_id_item) || !skill_id_item->valuestring || !skill_id_item->valuestring[0] ||
!cJSON_IsString(file_item) || !file_item->valuestring || !file_item->valuestring[0] ||
!cJSON_IsString(title_item) || !title_item->valuestring || !title_item->valuestring[0] ||
!cJSON_IsString(summary_item) || !summary_item->valuestring || !summary_item->valuestring[0]) {
cJSON_Delete(root);
cap_skill_write_error(output, output_size, "skill_id, file, title and summary are required", NULL);
cap_skill_write_error(output, output_size, "skill_id, file and summary are required", NULL);
return ESP_ERR_INVALID_ARG;
}
@@ -642,7 +639,6 @@ static esp_err_t cap_skill_register_execute(const char *input_json,
}
cJSON_AddStringToObject(skill, "id", skill_id_item->valuestring);
cJSON_AddStringToObject(skill, "file", file_item->valuestring);
cJSON_AddStringToObject(skill, "title", title_item->valuestring);
cJSON_AddStringToObject(skill, "summary", summary_item->valuestring);
cJSON_AddItemToArray(skills, skill);
@@ -677,7 +673,6 @@ static esp_err_t cap_skill_register_execute(const char *input_json,
}
cJSON_AddStringToObject(skill, "id", skill_id_item->valuestring);
cJSON_AddStringToObject(skill, "file", file_item->valuestring);
cJSON_AddStringToObject(skill, "title", title_item->valuestring);
cJSON_AddStringToObject(skill, "summary", summary_item->valuestring);
cJSON_Delete(root);
@@ -778,7 +773,7 @@ static const claw_cap_descriptor_t s_skill_descriptors[] = {
.kind = CLAW_CAP_KIND_CALLABLE,
.cap_flags = CLAW_CAP_FLAG_CALLABLE_BY_LLM,
.input_schema_json =
"{\"type\":\"object\",\"properties\":{\"skill_id\":{\"type\":\"string\"},\"file\":{\"type\":\"string\"},\"title\":{\"type\":\"string\"},\"summary\":{\"type\":\"string\"}},\"required\":[\"skill_id\",\"file\",\"title\",\"summary\"]}",
"{\"type\":\"object\",\"properties\":{\"skill_id\":{\"type\":\"string\"},\"file\":{\"type\":\"string\"},\"summary\":{\"type\":\"string\"}},\"required\":[\"skill_id\",\"file\",\"summary\"]}",
.execute = cap_skill_register_execute,
},
{
@@ -22,7 +22,6 @@ static struct {
struct arg_str *register_skill;
struct arg_str *unregister_skill;
struct arg_str *file;
struct arg_str *title;
struct arg_str *summary;
struct arg_str *activate;
struct arg_str *deactivate;
@@ -66,7 +65,6 @@ static int call_skill_cap(const char *cap_name, const char *input_json, const ch
static char *build_register_skill_json(const char *skill_id,
const char *file,
const char *title,
const char *summary)
{
cJSON *root = NULL;
@@ -79,7 +77,6 @@ static char *build_register_skill_json(const char *skill_id,
cJSON_AddStringToObject(root, "skill_id", skill_id);
cJSON_AddStringToObject(root, "file", file);
cJSON_AddStringToObject(root, "title", title);
cJSON_AddStringToObject(root, "summary", summary);
rendered = cJSON_PrintUnformatted(root);
cJSON_Delete(root);
@@ -134,14 +131,13 @@ static int skill_func(int argc, char **argv)
}
if (skill_args.register_skill->count) {
if (!skill_args.file->count || !skill_args.title->count || !skill_args.summary->count) {
printf("--register requires --file, --title and --summary\n");
if (!skill_args.file->count || !skill_args.summary->count) {
printf("--register requires --file and --summary\n");
return 1;
}
input_json = build_register_skill_json(skill_args.register_skill->sval[0],
skill_args.file->sval[0],
skill_args.title->sval[0],
skill_args.summary->sval[0]);
if (!input_json) {
printf("Out of memory\n");
@@ -211,7 +207,6 @@ void register_cap_skill(void)
skill_args.register_skill = arg_str0("r", "register", "<skill_id>", "Register one skill in the catalog");
skill_args.unregister_skill = arg_str0("u", "unregister", "<skill_id>", "Remove one skill from the catalog");
skill_args.file = arg_str0("f", "file", "<file.md>", "Skill markdown path relative to <storage_root>/skills");
skill_args.title = arg_str0("t", "title", "<title>", "Skill title for catalog registration");
skill_args.summary = arg_str0(NULL, "summary", "<summary>", "Skill summary for catalog registration");
skill_args.activate = arg_str0("a", "activate", "<skill_id>", "Activate one skill");
skill_args.deactivate = arg_str0("d", "deactivate", "<skill_id>", "Deactivate one skill");
@@ -224,7 +219,7 @@ void register_cap_skill(void)
.help = "Skill operations.\n"
"Examples:\n"
" skill --catalog\n"
" skill --register weather_v2 --file weather_v2.md --title \"Weather V2\" --summary \"Get forecast and alerts\"\n"
" skill --register weather_v2 --file weather_v2.md --summary \"Get forecast and alerts\"\n"
" skill --unregister weather_v2\n"
" skill --list --session default\n"
" skill --activate weather --session default\n"
@@ -352,6 +352,11 @@ static esp_err_t build_response_payload_json(const claw_core_request_t *request,
cJSON_Delete(root);
return ESP_ERR_NO_MEM;
}
if (response->error_message && response->error_message[0] &&
!cJSON_AddStringToObject(root, "error_message", response->error_message)) {
cJSON_Delete(root);
return ESP_ERR_NO_MEM;
}
payload_json = cJSON_PrintUnformatted(root);
cJSON_Delete(root);
@@ -370,18 +375,23 @@ static esp_err_t build_agent_response_event(const claw_core_request_t *request,
{
const char *channel = NULL;
const char *chat_id = NULL;
const char *text = NULL;
int64_t now_ms;
esp_err_t err;
if (!request || !response || !out_event || !out_payload_json ||
response->status != CLAW_CORE_RESPONSE_STATUS_OK ||
!response->text || !response->text[0]) {
if (!request || !response || !out_event || !out_payload_json) {
return ESP_ERR_INVALID_ARG;
}
memset(out_event, 0, sizeof(*out_event));
*out_payload_json = NULL;
text = (response->status == CLAW_CORE_RESPONSE_STATUS_OK) ?
response->text : response->error_message;
if (!text || !text[0]) {
return ESP_ERR_INVALID_ARG;
}
err = build_response_payload_json(request, response, out_payload_json);
if (err != ESP_OK) {
return err;
@@ -416,7 +426,7 @@ static esp_err_t build_agent_response_event(const claw_core_request_t *request,
}
out_event->timestamp_ms = now_ms;
out_event->session_policy = CLAW_EVENT_SESSION_POLICY_CHAT;
out_event->text = response->text;
out_event->text = (char *)text;
out_event->payload_json = *out_payload_json;
return ESP_OK;
@@ -433,10 +443,6 @@ static void publish_response_event_if_requested(const claw_core_request_item_t *
!(request->view.flags & CLAW_CORE_REQUEST_FLAG_PUBLISH_RESPONSE_EVENT)) {
return;
}
if (response->view.status != CLAW_CORE_RESPONSE_STATUS_OK ||
!response->view.text || !response->view.text[0]) {
return;
}
err = build_agent_response_event(&request->view,
&response->view,
@@ -946,7 +952,7 @@ static esp_err_t build_iteration_context(const claw_core_request_item_t *request
goto cleanup;
}
context_len = strlen(context.content);
ESP_LOGD(TAG,
ESP_LOGI(TAG,
"context_loaded request=%" PRIu32 " provider=%s context_kind=%s context_len=%u",
request->view.request_id,
provider->name,
@@ -26,7 +26,6 @@ static const char *SKILLS_LIST_FILE = "skills_list.json";
typedef struct {
char *id;
char *file;
char *title;
char *summary;
char **cap_groups;
size_t cap_group_count;
@@ -113,7 +112,6 @@ static void free_registry_entry(claw_skill_registry_entry_t *entry)
free(entry->id);
free(entry->file);
free(entry->title);
free(entry->summary);
free_string_array(entry->cap_groups, entry->cap_group_count);
memset(entry, 0, sizeof(*entry));
@@ -456,7 +454,7 @@ static esp_err_t validate_registry_entry(claw_skill_registry_entry_t *entry)
FILE *file = NULL;
size_t i;
if (!entry || !entry->id || !entry->file || !entry->title || !entry->summary) {
if (!entry || !entry->id || !entry->file || !entry->summary) {
return ESP_ERR_INVALID_ARG;
}
if (!skill_path_is_valid(entry->file) || !is_markdown_skill_file(entry->file)) {
@@ -543,9 +541,6 @@ static esp_err_t load_registry_from_json(void)
if (err == ESP_OK) {
err = json_dup_required_string(skill, "file", &entry->file);
}
if (err == ESP_OK) {
err = json_dup_required_string(skill, "title", &entry->title);
}
if (err == ESP_OK) {
err = json_dup_required_string(skill, "summary", &entry->summary);
}
@@ -746,8 +741,7 @@ static esp_err_t claw_skill_render_skills_list(char *buf, size_t size)
off += snprintf(buf + off,
size - off,
"- %s (%s): %s\n",
entry->title,
"- %s: %s\n",
entry->id,
entry->summary);
}
@@ -792,7 +786,7 @@ static esp_err_t claw_skill_build_prompt_block(const char *const *skill_ids,
size - off,
"%s### %s\n%s\n",
i == 0 ? "" : "\n",
entry->title,
entry->id,
content);
free(content);
}
@@ -1024,7 +1018,6 @@ static esp_err_t claw_skill_skills_list_collect(const claw_core_request_t *reque
content_size = 64;
for (size_t i = 0; i < s_skill.entry_count; i++) {
content_size += strlen(s_skill.entries[i].title ? s_skill.entries[i].title : "");
content_size += strlen(s_skill.entries[i].id ? s_skill.entries[i].id : "");
content_size += strlen(s_skill.entries[i].summary ? s_skill.entries[i].summary : "");
content_size += 16;