Compare commits
3 Commits
c6ecdb124c
...
3e709577f5
| Author | SHA1 | Date | |
|---|---|---|---|
| 3e709577f5 | |||
| b7ee0f9885 | |||
| be788be251 |
@ -1,6 +1,6 @@
|
||||
# Claude Code 插件高效运用指南
|
||||
|
||||
> 更新日期: 2026-04-13(新增 GSD 执行框架 + 完善工具链全景)
|
||||
> 更新日期: 2026-05-19(新增 embed-ai-tool 3 个嵌入式专项 skill:serial-monitor / rtos-debug / static-analysis)
|
||||
> 适用环境: macOS / Claude Code 2.1.79+ / ESP32 嵌入式开发
|
||||
|
||||
---
|
||||
@ -14,6 +14,7 @@
|
||||
| **GSD 执行框架** | **68 个 Skills** | 防上下文腐烂、任务编排、context monitor、原子提交 |
|
||||
| 自定义 Skills (~/.claude/skills/) | 11 个 | ESP32 专用 6 个 + RK3588/Linux 驱动 4 个 + 硬件驱动工作流 1 个 |
|
||||
| 第三方 Skills (~/.claude/skills/) | 7 个 | find-skills、tmux、summarize、tavily-research、embedded-systems、think、health |
|
||||
| **embed-ai-tool 嵌入式专项 Skills** | **3 个** | serial-monitor(串口抓包/日志分析)、rtos-debug(FreeRTOS 任务/栈/死锁)、static-analysis(cppcheck/clang-tidy/MISRA-C) |
|
||||
| 内置 Skills | 6 个 | simplify、loop、claude-api、schedule、update-config、keybindings-help |
|
||||
|
||||
---
|
||||
@ -567,6 +568,9 @@ allowed-tools: Bash, Read, Grep, Glob # 可选,限制可用工具
|
||||
| **embedded-systems** | 涉及固件开发、RTOS、中断处理、DMA、功耗优化、裸机编程、volatile 声明等通用嵌入式工程原则 |
|
||||
| **think** | 新功能、架构决策前。质疑需求、压力测试设计、提供 2-3 方案对比(不用于小 Bug 修复) |
|
||||
| **health** | Claude 行为异常、hooks 失效、Skills 配置冲突时。审计六层配置栈,按严重程度分级报告 |
|
||||
| **serial-monitor** | 说"抓串口"、"看串口日志"、"识别串口"、需要监控 UART 启动日志或断言输出 |
|
||||
| **rtos-debug** | 说"FreeRTOS 任务"、"栈水位"、"死锁"、"线程感知调试"、`pxCurrentTCB`、`uxTaskGetSystemState` |
|
||||
| **static-analysis** | 说"静态分析"、"cppcheck"、"clang-tidy"、"MISRA-C"、提交前缺陷扫描 |
|
||||
|
||||
### ESP32 Skills 与插件配合
|
||||
|
||||
@ -579,6 +583,14 @@ allowed-tools: Bash, Read, Grep, Glob # 可选,限制可用工具
|
||||
| esp-code-review | "帮我审查代码" | 先 esp-code-review → 再 `/review-pr` 双重审查 |
|
||||
| esp-driver | "写一个I2C驱动" | `/feature-dev` 设计 → esp-driver 生成 → `/code-review` 审查 |
|
||||
|
||||
### embed-ai-tool 嵌入式专项 Skills 与插件配合
|
||||
|
||||
| Skill | 触发方式 | 与插件配合 |
|
||||
|-------|---------|-----------|
|
||||
| **serial-monitor** | "抓串口" / "看启动日志" / "识别 ESP32 串口" | serial-monitor 抓日志 → esp-analyze-log 解析 → `/revise-claude-md` 记录 |
|
||||
| **rtos-debug** | "FreeRTOS 任务栈不够" / "死锁分析" / "看 pxCurrentTCB" | rtos-debug 分析任务/栈/优先级 → esp-troubleshoot 排障 → `/commit` 记录修复 |
|
||||
| **static-analysis** | "提交前静态扫描" / "cppcheck 一下" / "MISRA-C 检查" | static-analysis 扫描 → esp-code-review 二次审查 → `/commit-push-pr` 发布 |
|
||||
|
||||
### RK3588/Linux 驱动 Skills 与插件配合
|
||||
|
||||
| 自定义 Skill | 触发方式 | 与插件配合 |
|
||||
@ -697,6 +709,40 @@ npx skills add tw93/Waza@think -g -y
|
||||
npx skills add tw93/Waza@health -g -y
|
||||
```
|
||||
|
||||
#### 步骤 3.1.5:安装 embed-ai-tool 嵌入式专项 Skills(3 个)
|
||||
|
||||
这 3 个 skill 是手动从 GitHub 仓库 `LeoKemp223/embed-ai-tool` 复制的,不通过 `npx skills add`:
|
||||
|
||||
```bash
|
||||
# 临时 clone
|
||||
cd /tmp && git clone --depth 1 https://github.com/LeoKemp223/embed-ai-tool.git
|
||||
|
||||
# 复制 3 个 skill 到 ~/.claude/skills/
|
||||
cp -r /tmp/embed-ai-tool/skills/serial-monitor ~/.claude/skills/
|
||||
cp -r /tmp/embed-ai-tool/skills/rtos-debug ~/.claude/skills/
|
||||
cp -r /tmp/embed-ai-tool/skills/static-analysis ~/.claude/skills/
|
||||
|
||||
# 复制 shared 公共模块 (rtos-debug / static-analysis 依赖 tool_config.py 等)
|
||||
mkdir -p ~/.claude/skills/shared
|
||||
cp /tmp/embed-ai-tool/shared/*.py ~/.claude/skills/shared/
|
||||
cp /tmp/embed-ai-tool/shared/platform-compatibility.md ~/.claude/skills/shared/
|
||||
|
||||
# 清理
|
||||
rm -rf /tmp/embed-ai-tool
|
||||
```
|
||||
|
||||
**可选:安装这 3 个 skill 依赖的外部工具**(用到才装,不强制):
|
||||
|
||||
```bash
|
||||
# serial-monitor 依赖
|
||||
pip3 install pyserial
|
||||
|
||||
# static-analysis 依赖
|
||||
brew install cppcheck llvm # llvm 自带 clang-tidy
|
||||
```
|
||||
|
||||
> **注意**:脚本调用走 `python3 ~/.claude/skills/<skill>/scripts/<script>.py`,依赖未装时 skill 会提示安装命令。
|
||||
|
||||
#### 步骤 3.2:安装 GSD 执行框架
|
||||
|
||||
GSD(Get Shit Done)是防上下文腐烂的任务编排框架,安装后自动启用 context monitor hook:
|
||||
@ -743,6 +789,7 @@ idf.py --version # 应输出 ESP-IDF v5.4.2
|
||||
| 本指南文档 | Git 备份 | ❌ | clone 后自动生效 |
|
||||
| 插件代码(9 个插件) | 远程下载 | ✅ | 执行 `claude plugins install`(步骤 3) |
|
||||
| 第三方 Skills(7 个) | 远程下载 | ✅ | 执行 `npx skills add`(步骤 3.1) |
|
||||
| **embed-ai-tool 嵌入式专项 Skills(3 个)** | **GitHub clone** | **✅** | **执行 `git clone + cp`(步骤 3.1.5)** |
|
||||
| GSD 执行框架(68 个 Skills) | 远程下载 | ✅ | 执行 `npx get-shit-done-cc@latest`(步骤 3.2) |
|
||||
| Claude Code 程序 | npm 远程 | ✅ | 执行 `npm install -g` |
|
||||
| ESP-IDF v5.4.2 | GitHub | ✅ | 执行 `git clone` + `install.sh` |
|
||||
|
||||
|
After Width: | Height: | Size: 820 KiB |
|
After Width: | Height: | Size: 522 KiB |
|
After Width: | Height: | Size: 570 KiB |
|
After Width: | Height: | Size: 560 KiB |
|
After Width: | Height: | Size: 617 KiB |
|
After Width: | Height: | Size: 426 KiB |
|
After Width: | Height: | Size: 382 KiB |
|
After Width: | Height: | Size: 572 KiB |
|
Before Width: | Height: | Size: 3.3 MiB After Width: | Height: | Size: 3.4 MiB |
|
Before Width: | Height: | Size: 6.7 MiB After Width: | Height: | Size: 1.8 MiB |
@ -96,31 +96,35 @@ typedef struct {
|
||||
} eaf_emotion_map_t;
|
||||
|
||||
static const eaf_emotion_map_t s_emotion_map[] = {
|
||||
// 默认/积极 → m06
|
||||
{"neutral", "hiyori_m06.eaf"},
|
||||
{"happy", "hiyori_m06.eaf"},
|
||||
{"laughing", "hiyori_m06.eaf"},
|
||||
{"funny", "hiyori_m06.eaf"},
|
||||
{"loving", "hiyori_m06.eaf"},
|
||||
{"relaxed", "hiyori_m06.eaf"},
|
||||
{"delicious", "hiyori_m06.eaf"},
|
||||
{"kissy", "hiyori_m06.eaf"},
|
||||
{"confident", "hiyori_m06.eaf"},
|
||||
{"silly", "hiyori_m06.eaf"},
|
||||
{"blink", "hiyori_m06.eaf"},
|
||||
{"curious", "hiyori_m06.eaf"},
|
||||
// 思考/疲倦 → m07
|
||||
{"sleepy", "hiyori_m07.eaf"},
|
||||
{"thinking", "hiyori_m07.eaf"},
|
||||
{"confused", "hiyori_m07.eaf"},
|
||||
{"embarrassed", "hiyori_m07.eaf"},
|
||||
{"dizzy", "hiyori_m07.eaf"},
|
||||
// 负面/严肃 → 暂用 m07(m03 未导入)
|
||||
// 22 种情绪 → 8 张 EAF (m01~m08, 2026-05-20 全 8 张接入)
|
||||
// 注: 具体动作内容需要用户在 EAF Packer 中确认后调整, 当前是按情绪类别 +
|
||||
// 编号顺序的初版分配, 烧录后看效果再精准调整
|
||||
// 默认/积极组 (12 → m01..m05 均分, 每张约 2-3 种情绪)
|
||||
{"neutral", "hiyori_m01.eaf"},
|
||||
{"happy", "hiyori_m01.eaf"},
|
||||
{"blink", "hiyori_m01.eaf"},
|
||||
{"laughing", "hiyori_m02.eaf"},
|
||||
{"funny", "hiyori_m02.eaf"},
|
||||
{"curious", "hiyori_m02.eaf"},
|
||||
{"loving", "hiyori_m03.eaf"},
|
||||
{"relaxed", "hiyori_m03.eaf"},
|
||||
{"delicious", "hiyori_m04.eaf"},
|
||||
{"kissy", "hiyori_m04.eaf"},
|
||||
{"confident", "hiyori_m05.eaf"},
|
||||
{"silly", "hiyori_m05.eaf"},
|
||||
// 思考/疲倦组 (5) → m06
|
||||
{"sleepy", "hiyori_m06.eaf"},
|
||||
{"thinking", "hiyori_m06.eaf"},
|
||||
{"confused", "hiyori_m06.eaf"},
|
||||
{"embarrassed", "hiyori_m06.eaf"},
|
||||
{"dizzy", "hiyori_m06.eaf"},
|
||||
// 负面/严肃组 (3) → m07
|
||||
{"sad", "hiyori_m07.eaf"},
|
||||
{"crying", "hiyori_m07.eaf"},
|
||||
{"angry", "hiyori_m07.eaf"},
|
||||
{"surprised", "hiyori_m07.eaf"},
|
||||
{"shocked", "hiyori_m07.eaf"},
|
||||
// 惊讶组 (2) → m08
|
||||
{"surprised", "hiyori_m08.eaf"},
|
||||
{"shocked", "hiyori_m08.eaf"},
|
||||
};
|
||||
#define EMOTION_MAP_SIZE (sizeof(s_emotion_map) / sizeof(s_emotion_map[0]))
|
||||
|
||||
@ -153,6 +157,13 @@ static int find_cache_index_by_name(const char *name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// Fallback: 找不到精确匹配时 (例如 PoC 阶段 bin 里只有一张表情但代码映射到 m06/m07),
|
||||
// 用 cache 里第一个可用 EAF 替代, 保证设备能显示动画 (验证阶段方便用)
|
||||
// 注: 实际产品阶段所有情绪都该有对应 EAF, 这条 fallback 仅 PoC 兜底
|
||||
if (s_eaf_cache_count > 0 && s_eaf_cache[0].data) {
|
||||
ESP_LOGW(TAG, "Asset %s 不在 cache, fallback 到 %s", name, s_eaf_cache[0].name);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -186,8 +197,11 @@ static esp_err_t switch_emotion_by_asset(const char *asset_name) {
|
||||
};
|
||||
gfx_anim_set_src_desc(s_anim_obj, &src);
|
||||
|
||||
// 居中显示,hiyori 209×360 居中放 360×360 屏
|
||||
gfx_obj_align(s_anim_obj, GFX_ALIGN_CENTER, 0, 0);
|
||||
// 贴底显示, 让 240×320 数字人脚部紧贴屏底
|
||||
// 底部 40px (= 360-320) 给数字人脚部 (跟之前 360×360 EAF 视觉一致)
|
||||
// 顶部 40px 留透明空白 (chroma key 透明, 露出背景图)
|
||||
// 字幕 56 高度在数字人上层 (z-index), 显示时覆盖最下方约 56px (含脚部)
|
||||
gfx_obj_align(s_anim_obj, GFX_ALIGN_BOTTOM_MID, 0, 0);
|
||||
|
||||
// 全部帧 + EAF_DEFAULT_FPS + 永远循环
|
||||
gfx_anim_set_segment(s_anim_obj, 0, 0xFFFFFFFF, EAF_DEFAULT_FPS, true);
|
||||
@ -260,14 +274,22 @@ void ai_chat_screen_init(void) {
|
||||
}
|
||||
uint32_t file_count = header[12] | (header[13] << 8) | (header[14] << 16) | (header[15] << 24);
|
||||
ESP_LOGI(TAG, " MMAP file_count=%u", (unsigned)file_count);
|
||||
// 自动适配新旧两版 EAF Packer 的 MMAP 格式 (2026-05-20 新版 Packer entry 变 32B)
|
||||
// 旧版: header[8] = 0x10, entry 28B = name(16) + size(4) + offset(4) + pad(4)
|
||||
// 新版: header[8] = 0x14, entry 32B = name(16) + reserved(4) + size(4) + offset(4) + pad(4)
|
||||
const bool is_new_mmap = (header[8] == 0x14);
|
||||
const size_t ENTRY_SIZE = is_new_mmap ? 32 : 28;
|
||||
const size_t FSIZE_OFFSET = is_new_mmap ? 20 : 16;
|
||||
const size_t FOFFSET_OFFSET = is_new_mmap ? 24 : 20;
|
||||
ESP_LOGI(TAG, " MMAP 版本=%s, ENTRY_SIZE=%zu",
|
||||
is_new_mmap ? "新版(0x14)" : "旧版(0x10)", ENTRY_SIZE);
|
||||
// 跳过 reserved 16B 到 entry table 起点 (0x20)
|
||||
fseek(f, 0x20, SEEK_SET);
|
||||
const size_t ENTRY_SIZE = 28; // 16 + 4 + 4 + 4
|
||||
const size_t DATA_START = 0x20 + file_count * ENTRY_SIZE;
|
||||
|
||||
s_eaf_cache_count = 0;
|
||||
for (uint32_t i = 0; i < file_count; i++) {
|
||||
uint8_t entry[28];
|
||||
uint8_t entry[32]; // 按最大 entry 大小分配, 兼容旧/新版
|
||||
fseek(f, 0x20 + i * ENTRY_SIZE, SEEK_SET);
|
||||
if (fread(entry, 1, ENTRY_SIZE, f) != ENTRY_SIZE) {
|
||||
ESP_LOGE(TAG, " entry[%u] 读取失败", (unsigned)i);
|
||||
@ -276,8 +298,14 @@ void ai_chat_screen_init(void) {
|
||||
char name[17] = {0};
|
||||
memcpy(name, entry, 16);
|
||||
name[16] = '\0';
|
||||
uint32_t fsize = entry[16] | (entry[17] << 8) | (entry[18] << 16) | (entry[19] << 24);
|
||||
uint32_t foffset = entry[20] | (entry[21] << 8) | (entry[22] << 16) | (entry[23] << 24);
|
||||
uint32_t fsize = entry[FSIZE_OFFSET]
|
||||
| (entry[FSIZE_OFFSET + 1] << 8)
|
||||
| (entry[FSIZE_OFFSET + 2] << 16)
|
||||
| (entry[FSIZE_OFFSET + 3] << 24);
|
||||
uint32_t foffset = entry[FOFFSET_OFFSET]
|
||||
| (entry[FOFFSET_OFFSET + 1] << 8)
|
||||
| (entry[FOFFSET_OFFSET + 2] << 16)
|
||||
| (entry[FOFFSET_OFFSET + 3] << 24);
|
||||
|
||||
// 只缓存 .eaf 文件
|
||||
size_t nlen = strlen(name);
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 442 KiB |
|
Before Width: | Height: | Size: 398 KiB |