tirtc-device-builder 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +1 -1
- package/CHANGELOG.md +22 -0
- package/README.md +68 -18
- package/bin/agent-clients.js +141 -0
- package/bin/setup-esp32.js +43 -17
- package/bin/tirtc-device-builder.js +42 -17
- package/package.json +6 -2
- package/skills/tirtc-esp32-builder/SKILL.md +19 -8
- package/skills/tirtc-esp32-builder/USAGE.md +22 -7
- package/skills/tirtc-esp32-builder/VERSION +1 -1
- package/skills/tirtc-esp32-builder/assets/developer-intake-prompt.md +1 -1
- package/skills/tirtc-esp32-builder/assets/lckfb-szpi-esp32s3-portable-prompt.md +10 -8
- package/skills/tirtc-esp32-builder/assets/report-template.md +4 -1
- package/skills/tirtc-esp32-builder/references/environment.md +9 -3
- package/skills/tirtc-esp32-builder/references/firmware-delivery.md +83 -0
- package/skills/tirtc-esp32-builder/references/porting-risks.md +29 -1
- package/skills/tirtc-esp32-builder/references/product-controls.md +76 -0
- package/skills/tirtc-esp32-builder/references/reporting.md +7 -3
- package/skills/tirtc-esp32-builder/references/workflow.md +11 -0
- package/skills/tirtc-esp32-builder/scripts/firmware_identity.py +180 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
This project follows Semantic Versioning.
|
|
4
4
|
|
|
5
|
+
## 0.9.0
|
|
6
|
+
|
|
7
|
+
- Add native Skill-directory installation for Codex, Claude Code, OpenCode,
|
|
8
|
+
Gemini CLI, GitHub Copilot, Qwen Code, Windsurf Cascade, Cline, and Kiro.
|
|
9
|
+
- Add `--client` support to both `install` and the resumable `setup` workflow,
|
|
10
|
+
plus a `clients` command that reports resolved user directories.
|
|
11
|
+
- Keep one portable `SKILL.md` and supporting resource tree across clients,
|
|
12
|
+
while preserving explicit replacement and hardware-operation approvals.
|
|
13
|
+
|
|
14
|
+
## 0.8.1
|
|
15
|
+
|
|
16
|
+
- Separate rapid `idf.py flash` iteration from portable evidence-bundle export,
|
|
17
|
+
so active development keeps the normal `build/` tree and does not require a
|
|
18
|
+
new multi-BIN archive for every device test.
|
|
19
|
+
- Add deterministic ESP-IDF application-descriptor inspection for explicit
|
|
20
|
+
firmware versions, application-BIN SHA-256, and full ELF SHA-256 matching.
|
|
21
|
+
- Add product-control rules that distinguish MCU buttons, boot straps, reset
|
|
22
|
+
lines and PMIC/power-latch keys across baseboard and battery-carrier variants,
|
|
23
|
+
and route button gestures through the runtime's intent queue.
|
|
24
|
+
- Add trace-based diagnosis for repeated AI/H5 downlink audio across SDK
|
|
25
|
+
callbacks, bounded queues, playback, resampling, and codec/I2S writes.
|
|
26
|
+
|
|
5
27
|
## 0.8.0
|
|
6
28
|
|
|
7
29
|
- Add an evidence-backed board identity and curated registry workflow with exact,
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# TiRTC Device Builder
|
|
2
2
|
|
|
3
|
-
TiRTC Device Builder 用于把 ESP32-S3/ESP32-P4 开发板接入 TiRTC。输入可以只有开发板型号,也可以包含原理图、BSP、引脚表和外设示例。安装后的
|
|
3
|
+
TiRTC Device Builder 用于把 ESP32-S3/ESP32-P4 开发板接入 TiRTC。输入可以只有开发板型号,也可以包含原理图、BSP、引脚表和外设示例。安装后的 Agent Skill 会先检查环境、整理有依据的硬件事实,再生成或移植独立的 ESP-IDF 工程并完成板级适配和编译。烧录和实机验证只有在开发者明确给出目标串口并授权后才会执行。
|
|
4
4
|
|
|
5
5
|
当前仓库提供一个 Skill:
|
|
6
6
|
|
|
@@ -43,9 +43,15 @@ npm --version
|
|
|
43
43
|
npx --yes tirtc-device-builder@latest setup esp32 --install
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
默认安装到 Codex。使用其他客户端时增加 `--client`,例如:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx --yes tirtc-device-builder@latest setup esp32 --install --client qwen-code
|
|
50
|
+
```
|
|
51
|
+
|
|
46
52
|
这条命令会:
|
|
47
53
|
|
|
48
|
-
- 把 `tirtc-esp32-builder`
|
|
54
|
+
- 把 `tirtc-esp32-builder` 安装到所选 Agent 客户端的 Skill 目录;
|
|
49
55
|
- 下载 ESP32 Device Kit 并校验 SHA-256;
|
|
50
56
|
- 复用当前可用的 ESP-IDF 5.5.x,找不到时安装 ESP-IDF 5.5.4;
|
|
51
57
|
- 安装 Espressif 管理的 ESP32-S3 工具链;
|
|
@@ -59,21 +65,21 @@ npx --yes tirtc-device-builder@latest setup esp32 --install
|
|
|
59
65
|
```text
|
|
60
66
|
OVERALL: PASS
|
|
61
67
|
SETUP: READY
|
|
62
|
-
Start a new Codex session
|
|
68
|
+
Start a new Codex session, then ask it to use tirtc-esp32-builder.
|
|
63
69
|
```
|
|
64
70
|
|
|
65
71
|
如果看到 `OVERALL: NEEDS_SETUP`、`MISS` 或 `FAIL`,先看[常见问题](#常见问题)。
|
|
66
72
|
|
|
67
|
-
### 3. 重新打开
|
|
73
|
+
### 3. 重新打开 Agent 客户端
|
|
68
74
|
|
|
69
|
-
Skill
|
|
75
|
+
Skill 通常在 Agent 会话启动时被发现。安装完成后,关闭当前会话,再打开一个新会话。
|
|
70
76
|
|
|
71
|
-
### 4. 把板卡和目标告诉
|
|
77
|
+
### 4. 把板卡和目标告诉 Agent
|
|
72
78
|
|
|
73
79
|
把你已经掌握的信息填进下面的提示词即可,不用先查齐所有硬件参数。先指定工作区根目录,本地路径尽量相对工作区填写;不确定的内容写“未知”。可直接复制的版本见[开发板接入提示词](skills/tirtc-esp32-builder/assets/developer-intake-prompt.md)。
|
|
74
80
|
|
|
75
81
|
```text
|
|
76
|
-
请使用
|
|
82
|
+
请使用 tirtc-esp32-builder Skill 完成这块开发板的 TiRTC 移植。
|
|
77
83
|
|
|
78
84
|
开发板:
|
|
79
85
|
- 厂商、完整型号、PCB/硬件版本:<填写>
|
|
@@ -339,25 +345,52 @@ tirtc-device-builder setup esp32 --install
|
|
|
339
345
|
|
|
340
346
|
安装命令是 `npm install`,不要写成 `npm --install`。
|
|
341
347
|
|
|
342
|
-
只执行 `npm install --global tirtc-device-builder` 会安装 CLI,不会准备完整开发环境。随后还要运行 `setup esp32 --install`,由它安装
|
|
348
|
+
只执行 `npm install --global tirtc-device-builder` 会安装 CLI,不会准备完整开发环境。随后还要运行 `setup esp32 --install`,由它安装 Agent Skill、下载 Device Kit,并在需要时安装 ESP-IDF。npm 包没有 `preinstall`、`install` 或 `postinstall` 生命周期脚本,因此不会在安装 CLI 时改动这些目录。
|
|
343
349
|
|
|
344
350
|
安装公开包不需要执行 `npm login`;这个命令只和维护者发布新版本有关。
|
|
345
351
|
|
|
346
352
|
### 只安装 Skill
|
|
347
353
|
|
|
348
|
-
如果 ESP-IDF 和 Device Kit 已经由团队统一准备,可以只复制
|
|
354
|
+
如果 ESP-IDF 和 Device Kit 已经由团队统一准备,可以只复制 Agent Skill:
|
|
349
355
|
|
|
350
356
|
```bash
|
|
351
357
|
npx --yes tirtc-device-builder@latest install esp32
|
|
358
|
+
npx --yes tirtc-device-builder@latest install esp32 --client gemini
|
|
352
359
|
```
|
|
353
360
|
|
|
354
361
|
这条命令不安装 ESP-IDF,也不下载 Device Kit。完整的新用户环境仍建议使用 `setup esp32 --install`。
|
|
355
362
|
|
|
363
|
+
### 支持的 Agent 客户端
|
|
364
|
+
|
|
365
|
+
同一个 `tirtc-esp32-builder` Skill 会完整复制到所选客户端的原生目录,不维护客户端专用的内容副本。先用下面的命令查看当前机器解析出的目录:
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
npx --yes tirtc-device-builder@latest clients
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
| `--client` | 客户端 | 默认 Skill 根目录 |
|
|
372
|
+
|---|---|---|
|
|
373
|
+
| `codex` | Codex(默认) | `${CODEX_HOME:-~/.codex}/skills` |
|
|
374
|
+
| `claude-code` | Claude Code | `~/.claude/skills` |
|
|
375
|
+
| `opencode` | OpenCode | `${XDG_CONFIG_HOME:-~/.config}/opencode/skills` |
|
|
376
|
+
| `gemini` | Gemini CLI | `~/.gemini/skills` |
|
|
377
|
+
| `copilot` | GitHub Copilot | `~/.copilot/skills` |
|
|
378
|
+
| `qwen-code` | Qwen Code | `~/.qwen/skills` |
|
|
379
|
+
| `windsurf` | Windsurf Cascade | `~/.codeium/windsurf/skills` |
|
|
380
|
+
| `cline` | Cline | `~/.cline/skills` |
|
|
381
|
+
| `kiro` | Kiro | `~/.kiro/skills` |
|
|
382
|
+
|
|
383
|
+
也可以使用 `claude`、`gemini-cli`、`github-copilot`、`qwen`、`cascade`、`kiro-cli` 等别名。`--skills-dir` 的优先级高于默认目录,适合项目级安装或客户端使用非默认配置目录的情况。
|
|
384
|
+
|
|
385
|
+
`--client` 只选择 Skill 的安装目录和提示文案,不绑定模型服务。DeepSeek、Qwen、GLM、Kimi、OpenAI 或 Claude 等模型仍在对应客户端中配置;只要客户端能发现 Skill 并允许文件、终端等所需工具,就继续使用同一份工作流。
|
|
386
|
+
|
|
387
|
+
Cline 当前还需要在 `Settings → Features → Enable Skills` 中启用实验性 Skills 功能;安装器不会修改客户端自身的权限或功能开关。
|
|
388
|
+
|
|
356
389
|
### 默认目录
|
|
357
390
|
|
|
358
391
|
| 内容 | 默认位置 |
|
|
359
392
|
|---|---|
|
|
360
|
-
|
|
|
393
|
+
| Agent Skill | 上表中所选目录下的 `tirtc-esp32-builder` |
|
|
361
394
|
| 托管根目录 | `~/.tirtc-device-builder` |
|
|
362
395
|
| Device Kit | `~/.tirtc-device-builder/kits/esp32s3/1.1.1` |
|
|
363
396
|
| ESP-IDF | `~/.tirtc-device-builder/esp-idf-v5.5.4` |
|
|
@@ -436,6 +469,7 @@ npx --yes tirtc-device-builder@latest setup esp32
|
|
|
436
469
|
```bash
|
|
437
470
|
npx --yes tirtc-device-builder@latest --help
|
|
438
471
|
npx --yes tirtc-device-builder@latest list
|
|
472
|
+
npx --yes tirtc-device-builder@latest clients
|
|
439
473
|
npx --yes tirtc-device-builder@latest setup esp32 --help
|
|
440
474
|
```
|
|
441
475
|
|
|
@@ -549,7 +583,19 @@ python3 ~/.codex/skills/tirtc-esp32-builder/scripts/install_video_gate.py .
|
|
|
549
583
|
idf.py build
|
|
550
584
|
```
|
|
551
585
|
|
|
552
|
-
|
|
586
|
+
只安装实际请求能力的门禁。编译后先核对固件内嵌版本、应用 BIN
|
|
587
|
+
SHA-256 和完整 ELF SHA-256:
|
|
588
|
+
|
|
589
|
+
```bash
|
|
590
|
+
python3 ~/.codex/skills/tirtc-esp32-builder/scripts/firmware_identity.py \
|
|
591
|
+
build/<app>.bin --elf build/<app>.elf --expect-version <expected-version>
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
随后按
|
|
595
|
+
[`firmware-delivery.md`](skills/tirtc-esp32-builder/references/firmware-delivery.md)
|
|
596
|
+
选择快速真机迭代或可移植证据包;该文档也规定从 `build/` 记录迁移到
|
|
597
|
+
`artifacts/` 时如何保持 Hardware IR 和报告一致。编译成功但语义门禁缺失
|
|
598
|
+
或失败时只能记录 `COMPILE_PASS / CAPABILITY_BLOCKED`。
|
|
553
599
|
|
|
554
600
|
生成器会把 TiRTC SDK 复制到工程的 `third_party/tirtc/`。此后工程不再依赖 `tirtc-server-example`,但换机编译仍需准备兼容的 ESP-IDF 5.5.x 工具链。
|
|
555
601
|
|
|
@@ -599,7 +645,10 @@ cd /absolute/path/my-esp32-device
|
|
|
599
645
|
idf.py -p /dev/ttyACM0 flash monitor
|
|
600
646
|
```
|
|
601
647
|
|
|
602
|
-
|
|
648
|
+
普通开发烧录优先使用这一条命令,让 ESP-IDF 从当前构建目录解析
|
|
649
|
+
bootloader、分区表和应用镜像;无需手工维护多条 `esptool.py` 地址参数。
|
|
650
|
+
使用 `Ctrl+]` 退出 ESP-IDF Monitor。多个串口同时存在时,需要通过 USB
|
|
651
|
+
拔插、设备标识或芯片探测确认目标,不能默认烧录第一个端口。
|
|
603
652
|
|
|
604
653
|
### Wi-Fi 凭证和设备绑定
|
|
605
654
|
|
|
@@ -744,15 +793,16 @@ npx --yes tirtc-device-builder@latest setup esp32 --install \
|
|
|
744
793
|
|
|
745
794
|
如果指定目录已存在但内容不完整或版本错误,安装器会拒绝覆盖。换一个空的新路径,或先人工备份原目录。
|
|
746
795
|
|
|
747
|
-
### 安装完成后
|
|
796
|
+
### 安装完成后 Agent 客户端找不到 Skill
|
|
748
797
|
|
|
749
|
-
关闭当前
|
|
798
|
+
关闭当前 Agent 会话并重新打开。先查看客户端目录,再检查对应文件是否存在:
|
|
750
799
|
|
|
751
800
|
```bash
|
|
752
|
-
|
|
801
|
+
npx --yes tirtc-device-builder@latest clients
|
|
802
|
+
ls -l <skills-dir>/tirtc-esp32-builder/SKILL.md
|
|
753
803
|
```
|
|
754
804
|
|
|
755
|
-
|
|
805
|
+
确认安装和启动的是同一个客户端,例如 Qwen Code 要使用 `--client qwen-code`。设置过 `CODEX_HOME`、`XDG_CONFIG_HOME` 或 `--skills-dir` 时,要检查对应目录。项目级目录由客户端自身规则决定,可以用 `--skills-dir` 显式指定。
|
|
756
806
|
|
|
757
807
|
### 已有 Skill,安装器拒绝覆盖
|
|
758
808
|
|
|
@@ -911,8 +961,8 @@ metadata 中的版本、标签、上游 commit 和期望 SHA-256 必须与本地
|
|
|
911
961
|
|
|
912
962
|
```bash
|
|
913
963
|
npm test
|
|
914
|
-
git tag -a v0.
|
|
915
|
-
git push origin v0.
|
|
964
|
+
git tag -a v0.9.0 -m "v0.9.0"
|
|
965
|
+
git push origin v0.9.0
|
|
916
966
|
```
|
|
917
967
|
|
|
918
968
|
不要重复发布已经存在的 npm 版本。版本变化同步更新 `package.json`、`.codex-plugin/plugin.json` 和发布说明。
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
export const DEFAULT_AGENT_CLIENT = "codex";
|
|
5
|
+
|
|
6
|
+
const CLIENTS = [
|
|
7
|
+
{
|
|
8
|
+
id: "codex",
|
|
9
|
+
displayName: "Codex",
|
|
10
|
+
aliases: ["codex"],
|
|
11
|
+
skillsDir(environment, home) {
|
|
12
|
+
const codexHome = environment.CODEX_HOME
|
|
13
|
+
? resolve(environment.CODEX_HOME)
|
|
14
|
+
: join(home, ".codex");
|
|
15
|
+
return join(codexHome, "skills");
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: "claude-code",
|
|
20
|
+
displayName: "Claude Code",
|
|
21
|
+
aliases: ["claude-code", "claude"],
|
|
22
|
+
skillsDir(_environment, home) {
|
|
23
|
+
return join(home, ".claude", "skills");
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: "opencode",
|
|
28
|
+
displayName: "OpenCode",
|
|
29
|
+
aliases: ["opencode", "open-code"],
|
|
30
|
+
skillsDir(environment, home) {
|
|
31
|
+
const configHome = environment.XDG_CONFIG_HOME
|
|
32
|
+
? resolve(environment.XDG_CONFIG_HOME)
|
|
33
|
+
: join(home, ".config");
|
|
34
|
+
return join(configHome, "opencode", "skills");
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "gemini",
|
|
39
|
+
displayName: "Gemini CLI",
|
|
40
|
+
aliases: ["gemini", "gemini-cli"],
|
|
41
|
+
skillsDir(_environment, home) {
|
|
42
|
+
return join(home, ".gemini", "skills");
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: "copilot",
|
|
47
|
+
displayName: "GitHub Copilot",
|
|
48
|
+
aliases: ["copilot", "github-copilot"],
|
|
49
|
+
skillsDir(_environment, home) {
|
|
50
|
+
return join(home, ".copilot", "skills");
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: "qwen-code",
|
|
55
|
+
displayName: "Qwen Code",
|
|
56
|
+
aliases: ["qwen-code", "qwen"],
|
|
57
|
+
skillsDir(_environment, home) {
|
|
58
|
+
return join(home, ".qwen", "skills");
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: "windsurf",
|
|
63
|
+
displayName: "Windsurf Cascade",
|
|
64
|
+
aliases: ["windsurf", "cascade"],
|
|
65
|
+
skillsDir(_environment, home) {
|
|
66
|
+
return join(home, ".codeium", "windsurf", "skills");
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: "cline",
|
|
71
|
+
displayName: "Cline",
|
|
72
|
+
aliases: ["cline"],
|
|
73
|
+
activationNote:
|
|
74
|
+
"Cline may require Settings > Features > Enable Skills before discovery.",
|
|
75
|
+
skillsDir(_environment, home) {
|
|
76
|
+
return join(home, ".cline", "skills");
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "kiro",
|
|
81
|
+
displayName: "Kiro",
|
|
82
|
+
aliases: ["kiro", "kiro-cli"],
|
|
83
|
+
skillsDir(_environment, home) {
|
|
84
|
+
return join(home, ".kiro", "skills");
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
const CLIENT_BY_ALIAS = new Map(
|
|
90
|
+
CLIENTS.flatMap((client) =>
|
|
91
|
+
client.aliases.map((alias) => [alias, client]),
|
|
92
|
+
),
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
function userHome(environment) {
|
|
96
|
+
if (process.platform === "win32") {
|
|
97
|
+
if (environment.USERPROFILE) {
|
|
98
|
+
return resolve(environment.USERPROFILE);
|
|
99
|
+
}
|
|
100
|
+
if (environment.HOMEDRIVE && environment.HOMEPATH) {
|
|
101
|
+
return resolve(`${environment.HOMEDRIVE}${environment.HOMEPATH}`);
|
|
102
|
+
}
|
|
103
|
+
} else if (environment.HOME) {
|
|
104
|
+
return resolve(environment.HOME);
|
|
105
|
+
}
|
|
106
|
+
return homedir();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function listAgentClients() {
|
|
110
|
+
return CLIENTS;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function resolveAgentClient(identifier) {
|
|
114
|
+
return CLIENT_BY_ALIAS.get(String(identifier).trim().toLowerCase()) ?? null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function requireAgentClient(identifier) {
|
|
118
|
+
const client = resolveAgentClient(identifier);
|
|
119
|
+
if (!client) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
`unsupported client: ${identifier}; supported clients: ${CLIENTS.map((item) => item.id).join(", ")}`,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
return client;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function defaultSkillsDir(client, environment = process.env) {
|
|
128
|
+
const resolvedClient =
|
|
129
|
+
typeof client === "string" ? requireAgentClient(client) : client;
|
|
130
|
+
return resolvedClient.skillsDir(environment, userHome(environment));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function clientSessionHint(client, skillName) {
|
|
134
|
+
const lines = [
|
|
135
|
+
`Start a new ${client.displayName} session, then ask it to use ${skillName}.`,
|
|
136
|
+
];
|
|
137
|
+
if (client.activationNote) {
|
|
138
|
+
lines.push(client.activationNote);
|
|
139
|
+
}
|
|
140
|
+
return lines.join("\n");
|
|
141
|
+
}
|
package/bin/setup-esp32.js
CHANGED
|
@@ -10,6 +10,13 @@ import {
|
|
|
10
10
|
} from "node:fs";
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
12
|
import { delimiter, dirname, join, resolve } from "node:path";
|
|
13
|
+
import {
|
|
14
|
+
clientSessionHint,
|
|
15
|
+
DEFAULT_AGENT_CLIENT,
|
|
16
|
+
defaultSkillsDir,
|
|
17
|
+
listAgentClients,
|
|
18
|
+
requireAgentClient,
|
|
19
|
+
} from "./agent-clients.js";
|
|
13
20
|
import { ESP32_KIT } from "./esp32-kit-metadata.js";
|
|
14
21
|
|
|
15
22
|
const EXPECTED_IDF_LINE = "5.5";
|
|
@@ -43,10 +50,10 @@ function setupRootFrom(environment) {
|
|
|
43
50
|
);
|
|
44
51
|
}
|
|
45
52
|
|
|
46
|
-
function takeValue(args, index, option) {
|
|
53
|
+
function takeValue(args, index, option, kind = "a path") {
|
|
47
54
|
const value = args[index + 1];
|
|
48
55
|
if (!value || value.startsWith("--")) {
|
|
49
|
-
throw new Error(`${option} requires
|
|
56
|
+
throw new Error(`${option} requires ${kind}`);
|
|
50
57
|
}
|
|
51
58
|
return value;
|
|
52
59
|
}
|
|
@@ -54,13 +61,15 @@ function takeValue(args, index, option) {
|
|
|
54
61
|
export function parseSetupOptions(args, defaults = {}) {
|
|
55
62
|
const environment = defaults.environment ?? process.env;
|
|
56
63
|
const options = {
|
|
64
|
+
client:
|
|
65
|
+
defaults.client ?? requireAgentClient(DEFAULT_AGENT_CLIENT),
|
|
57
66
|
forceSkill: false,
|
|
58
67
|
help: false,
|
|
59
68
|
idfDir: null,
|
|
60
69
|
install: false,
|
|
61
70
|
kitArchive: null,
|
|
62
71
|
rootDir: setupRootFrom(environment),
|
|
63
|
-
skillsDir:
|
|
72
|
+
skillsDir: null,
|
|
64
73
|
thingConnectRoot: null,
|
|
65
74
|
};
|
|
66
75
|
|
|
@@ -78,6 +87,13 @@ export function parseSetupOptions(args, defaults = {}) {
|
|
|
78
87
|
options.forceSkill = true;
|
|
79
88
|
continue;
|
|
80
89
|
}
|
|
90
|
+
if (argument === "--client") {
|
|
91
|
+
options.client = requireAgentClient(
|
|
92
|
+
takeValue(args, index, argument, "a name"),
|
|
93
|
+
);
|
|
94
|
+
index += 1;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
81
97
|
if (
|
|
82
98
|
argument === "--root" ||
|
|
83
99
|
argument === "--skills-dir" ||
|
|
@@ -103,9 +119,7 @@ export function parseSetupOptions(args, defaults = {}) {
|
|
|
103
119
|
throw new Error(`unknown setup option: ${argument}`);
|
|
104
120
|
}
|
|
105
121
|
|
|
106
|
-
|
|
107
|
-
throw new Error("setup requires a default skills directory");
|
|
108
|
-
}
|
|
122
|
+
options.skillsDir ??= defaultSkillsDir(options.client, environment);
|
|
109
123
|
if (options.forceSkill && !options.install) {
|
|
110
124
|
throw new Error("--force-skill requires --install");
|
|
111
125
|
}
|
|
@@ -123,8 +137,9 @@ Usage:
|
|
|
123
137
|
|
|
124
138
|
Options:
|
|
125
139
|
--install Install missing user-space components
|
|
140
|
+
--client <name> Agent client (default: codex)
|
|
126
141
|
--root <path> Managed files (default: ~/.tirtc-device-builder)
|
|
127
|
-
--skills-dir <path>
|
|
142
|
+
--skills-dir <path> Override the selected client's Skill directory
|
|
128
143
|
--thing-connect-root <path> Reuse an existing Device Kit or legacy workspace
|
|
129
144
|
--kit-archive <path> Install from a local verified Kit archive
|
|
130
145
|
--idf-dir <path> Reuse or install ESP-IDF at this path
|
|
@@ -133,12 +148,13 @@ Options:
|
|
|
133
148
|
|
|
134
149
|
Examples:
|
|
135
150
|
npx tirtc-device-builder setup esp32
|
|
136
|
-
npx tirtc-device-builder setup esp32 --install
|
|
151
|
+
npx tirtc-device-builder setup esp32 --install --client gemini
|
|
137
152
|
npx tirtc-device-builder setup esp32 --install --root /opt/tirtc-dev
|
|
138
153
|
|
|
139
154
|
Check mode is read-only. --install downloads the pinned ESP32 Device Kit,
|
|
140
155
|
may clone ESP-IDF v5.5.4, runs Espressif's user-space tool installer, and
|
|
141
|
-
installs the
|
|
156
|
+
installs the Agent Skill. It does not run sudo or edit shell profiles.
|
|
157
|
+
Supported clients: ${listAgentClients().map((client) => client.id).join(", ")}.`);
|
|
142
158
|
}
|
|
143
159
|
|
|
144
160
|
function printCheck(status, name, detail) {
|
|
@@ -420,6 +436,7 @@ function writeManagedEnvironment(context, packageVersion) {
|
|
|
420
436
|
device_kit_version: context.deviceKit.version,
|
|
421
437
|
device_kit_root: context.thingConnectRoot,
|
|
422
438
|
thing_connect_root: context.thingConnectRoot,
|
|
439
|
+
client: context.client.id,
|
|
423
440
|
skills_dir: context.skillsDir,
|
|
424
441
|
};
|
|
425
442
|
writeAtomic(
|
|
@@ -542,6 +559,7 @@ function setupContext(options, runtime) {
|
|
|
542
559
|
const skillVersion = readSkillVersion(skillTarget);
|
|
543
560
|
return {
|
|
544
561
|
activeIdf: active,
|
|
562
|
+
client: options.client,
|
|
545
563
|
configPath,
|
|
546
564
|
idf,
|
|
547
565
|
idfDir,
|
|
@@ -569,8 +587,8 @@ function printState(context, runtime) {
|
|
|
569
587
|
printCheck("INFO", "setup root", context.rootDir);
|
|
570
588
|
printCheck(
|
|
571
589
|
context.skillReady ? "PASS" : "MISS",
|
|
572
|
-
"
|
|
573
|
-
`${context.skillTarget} (version ${
|
|
590
|
+
"Agent Skill",
|
|
591
|
+
`${context.client.displayName}: ${context.skillTarget} (version ${
|
|
574
592
|
context.skillVersion || "missing"
|
|
575
593
|
}; expected ${runtime.packageVersion})`,
|
|
576
594
|
);
|
|
@@ -639,13 +657,13 @@ function installSkill(options, context, runtime) {
|
|
|
639
657
|
const present = existsSync(join(context.skillTarget, "SKILL.md"));
|
|
640
658
|
if (present && context.skillReady && !options.forceSkill) {
|
|
641
659
|
console.log(
|
|
642
|
-
`SKIP
|
|
660
|
+
`SKIP ${context.client.displayName} Skill ${runtime.packageVersion} already exists: ${context.skillTarget}`,
|
|
643
661
|
);
|
|
644
662
|
return;
|
|
645
663
|
}
|
|
646
664
|
if (present && !options.forceSkill) {
|
|
647
665
|
throw new Error(
|
|
648
|
-
|
|
666
|
+
`${context.client.displayName} Skill at ${context.skillTarget} has version ${
|
|
649
667
|
context.skillVersion || "missing"
|
|
650
668
|
}, expected ${runtime.packageVersion}; rerun with --force-skill to replace it`,
|
|
651
669
|
);
|
|
@@ -654,6 +672,8 @@ function installSkill(options, context, runtime) {
|
|
|
654
672
|
runtime.cliPath,
|
|
655
673
|
"install",
|
|
656
674
|
runtime.platform.name,
|
|
675
|
+
"--client",
|
|
676
|
+
context.client.id,
|
|
657
677
|
"--skills-dir",
|
|
658
678
|
options.skillsDir,
|
|
659
679
|
];
|
|
@@ -830,8 +850,8 @@ export function runEsp32Setup(args, input) {
|
|
|
830
850
|
let options;
|
|
831
851
|
try {
|
|
832
852
|
options = parseSetupOptions(args, {
|
|
853
|
+
client: input.defaultClient,
|
|
833
854
|
environment: runtime.environment,
|
|
834
|
-
skillsDir: input.defaultSkillsDir,
|
|
835
855
|
});
|
|
836
856
|
} catch (error) {
|
|
837
857
|
console.error(
|
|
@@ -849,8 +869,12 @@ export function runEsp32Setup(args, input) {
|
|
|
849
869
|
if (!options.install) {
|
|
850
870
|
if (!isReady(context)) {
|
|
851
871
|
console.log("OVERALL: NEEDS_SETUP");
|
|
872
|
+
const clientOption =
|
|
873
|
+
context.client.id === "codex"
|
|
874
|
+
? ""
|
|
875
|
+
: ` --client ${context.client.id}`;
|
|
852
876
|
console.log(
|
|
853
|
-
`NEXT: npx tirtc-device-builder@${runtime.packageVersion} setup esp32 --install`,
|
|
877
|
+
`NEXT: npx tirtc-device-builder@${runtime.packageVersion} setup esp32 --install${clientOption}`,
|
|
854
878
|
);
|
|
855
879
|
return 1;
|
|
856
880
|
}
|
|
@@ -867,7 +891,9 @@ export function runEsp32Setup(args, input) {
|
|
|
867
891
|
return 1;
|
|
868
892
|
}
|
|
869
893
|
console.log("INSTALL PLAN:");
|
|
870
|
-
console.log(
|
|
894
|
+
console.log(
|
|
895
|
+
` Skill: ${context.skillTarget} (${context.client.displayName})`,
|
|
896
|
+
);
|
|
871
897
|
console.log(` Device Kit: ${context.thingConnectRoot} (${ESP32_KIT.version})`);
|
|
872
898
|
console.log(` ESP-IDF: ${context.idfDir} (${PINNED_IDF_VERSION})`);
|
|
873
899
|
console.log(` IDF tools: ${context.idfToolsPath || "existing default"}`);
|
|
@@ -917,6 +943,6 @@ export function runEsp32Setup(args, input) {
|
|
|
917
943
|
}
|
|
918
944
|
console.log("SETUP: READY");
|
|
919
945
|
console.log(`Environment helper: ${join(context.rootDir, "env.sh")}`);
|
|
920
|
-
console.log(
|
|
946
|
+
console.log(clientSessionHint(context.client, runtime.platform.skill));
|
|
921
947
|
return 0;
|
|
922
948
|
}
|
|
@@ -9,9 +9,15 @@ import {
|
|
|
9
9
|
renameSync,
|
|
10
10
|
rmSync,
|
|
11
11
|
} from "node:fs";
|
|
12
|
-
import { homedir } from "node:os";
|
|
13
12
|
import { dirname, join, resolve } from "node:path";
|
|
14
13
|
import { fileURLToPath } from "node:url";
|
|
14
|
+
import {
|
|
15
|
+
clientSessionHint,
|
|
16
|
+
DEFAULT_AGENT_CLIENT,
|
|
17
|
+
defaultSkillsDir,
|
|
18
|
+
listAgentClients,
|
|
19
|
+
requireAgentClient,
|
|
20
|
+
} from "./agent-clients.js";
|
|
15
21
|
import { runEsp32Setup } from "./setup-esp32.js";
|
|
16
22
|
|
|
17
23
|
const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
@@ -35,7 +41,8 @@ function printHelp() {
|
|
|
35
41
|
|
|
36
42
|
Usage:
|
|
37
43
|
tirtc-device-builder list
|
|
38
|
-
tirtc-device-builder
|
|
44
|
+
tirtc-device-builder clients
|
|
45
|
+
tirtc-device-builder install <platform> [--client <name>] [--skills-dir <path>] [--force]
|
|
39
46
|
tirtc-device-builder setup <platform> [setup options]
|
|
40
47
|
tirtc-device-builder doctor <platform> [doctor options]
|
|
41
48
|
tirtc-device-builder boards <platform> <list|validate|match|init-identity|candidate> [options]
|
|
@@ -46,15 +53,18 @@ Platforms:
|
|
|
46
53
|
|
|
47
54
|
Examples:
|
|
48
55
|
npx tirtc-device-builder install esp32
|
|
56
|
+
npx tirtc-device-builder install esp32 --client qwen-code
|
|
49
57
|
npx tirtc-device-builder setup esp32
|
|
50
|
-
npx tirtc-device-builder setup esp32 --install
|
|
58
|
+
npx tirtc-device-builder setup esp32 --install --client gemini
|
|
51
59
|
npx tirtc-device-builder install esp32 --skills-dir /absolute/path/skills
|
|
52
60
|
npx tirtc-device-builder doctor esp32 --project /absolute/path/project
|
|
53
61
|
npx tirtc-device-builder boards esp32 match --identity ./board-identity.json
|
|
54
62
|
|
|
55
|
-
Install defaults to
|
|
56
|
-
|
|
57
|
-
|
|
63
|
+
Install defaults to the selected client's user Skill directory; Codex is the
|
|
64
|
+
default client. Run "clients" to list supported names and resolved directories.
|
|
65
|
+
Existing skills are preserved unless --force is explicitly supplied. Setup
|
|
66
|
+
checks are read-only; setup --install installs missing user-space components
|
|
67
|
+
without running sudo.`);
|
|
58
68
|
}
|
|
59
69
|
|
|
60
70
|
function fail(message) {
|
|
@@ -71,17 +81,11 @@ function resolvePlatform(identifier) {
|
|
|
71
81
|
return null;
|
|
72
82
|
}
|
|
73
83
|
|
|
74
|
-
function defaultSkillsDir() {
|
|
75
|
-
const codexHome = process.env.CODEX_HOME
|
|
76
|
-
? resolve(process.env.CODEX_HOME)
|
|
77
|
-
: join(homedir(), ".codex");
|
|
78
|
-
return join(codexHome, "skills");
|
|
79
|
-
}
|
|
80
|
-
|
|
81
84
|
function parseInstallOptions(args) {
|
|
82
85
|
const options = {
|
|
86
|
+
client: requireAgentClient(DEFAULT_AGENT_CLIENT),
|
|
83
87
|
force: false,
|
|
84
|
-
skillsDir:
|
|
88
|
+
skillsDir: null,
|
|
85
89
|
};
|
|
86
90
|
|
|
87
91
|
for (let index = 0; index < args.length; index += 1) {
|
|
@@ -90,6 +94,15 @@ function parseInstallOptions(args) {
|
|
|
90
94
|
options.force = true;
|
|
91
95
|
continue;
|
|
92
96
|
}
|
|
97
|
+
if (argument === "--client") {
|
|
98
|
+
const value = args[index + 1];
|
|
99
|
+
if (!value || value.startsWith("--")) {
|
|
100
|
+
throw new Error("--client requires a name");
|
|
101
|
+
}
|
|
102
|
+
options.client = requireAgentClient(value);
|
|
103
|
+
index += 1;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
93
106
|
if (argument === "--skills-dir") {
|
|
94
107
|
const value = args[index + 1];
|
|
95
108
|
if (!value || value.startsWith("--")) {
|
|
@@ -102,6 +115,7 @@ function parseInstallOptions(args) {
|
|
|
102
115
|
throw new Error(`unknown install option: ${argument}`);
|
|
103
116
|
}
|
|
104
117
|
|
|
118
|
+
options.skillsDir ??= defaultSkillsDir(options.client);
|
|
105
119
|
return options;
|
|
106
120
|
}
|
|
107
121
|
|
|
@@ -151,8 +165,10 @@ function installSkill(platform, options) {
|
|
|
151
165
|
if (movedExisting && existsSync(backup)) {
|
|
152
166
|
rmSync(backup, { force: true, recursive: true });
|
|
153
167
|
}
|
|
154
|
-
console.log(
|
|
155
|
-
|
|
168
|
+
console.log(
|
|
169
|
+
`Installed ${platform.skill} ${PACKAGE.version} for ${options.client.displayName} to ${target}`,
|
|
170
|
+
);
|
|
171
|
+
console.log(clientSessionHint(options.client, platform.skill));
|
|
156
172
|
}
|
|
157
173
|
|
|
158
174
|
function runDoctor(platform, args) {
|
|
@@ -214,6 +230,15 @@ function main(args) {
|
|
|
214
230
|
}
|
|
215
231
|
return 0;
|
|
216
232
|
}
|
|
233
|
+
if (args[0] === "clients") {
|
|
234
|
+
for (const client of listAgentClients()) {
|
|
235
|
+
const marker = client.id === DEFAULT_AGENT_CLIENT ? " (default)" : "";
|
|
236
|
+
console.log(
|
|
237
|
+
`${client.id}\t${client.displayName}${marker}\t${defaultSkillsDir(client)}`,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
return 0;
|
|
241
|
+
}
|
|
217
242
|
|
|
218
243
|
const [command, identifier, ...rest] = args;
|
|
219
244
|
if (
|
|
@@ -244,7 +269,7 @@ function main(args) {
|
|
|
244
269
|
}
|
|
245
270
|
return runEsp32Setup(rest, {
|
|
246
271
|
cliPath: fileURLToPath(import.meta.url),
|
|
247
|
-
|
|
272
|
+
defaultClient: requireAgentClient(DEFAULT_AGENT_CLIENT),
|
|
248
273
|
packageRoot: PACKAGE_ROOT,
|
|
249
274
|
packageVersion: PACKAGE.version,
|
|
250
275
|
platform,
|