tirtc-device-builder 0.8.1 → 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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tirtc-device-builder",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "Codex workflows for building and validating TiRTC device firmware across supported chip platforms.",
5
5
  "author": {
6
6
  "name": "TangeAI",
package/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
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
+
5
14
  ## 0.8.1
6
15
 
7
16
  - Separate rapid `idf.py flash` iteration from portable evidence-bundle export,
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、引脚表和外设示例。安装后的 Codex Skill 会先检查环境、整理有依据的硬件事实,再生成或移植独立的 ESP-IDF 工程并完成板级适配和编译。烧录和实机验证只有在开发者明确给出目标串口并授权后才会执行。
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` 安装到 Codex Skill 目录;
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 and invoke $tirtc-esp32-builder.
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. 重新打开 Codex
73
+ ### 3. 重新打开 Agent 客户端
68
74
 
69
- Skill Codex 会话启动时被发现。安装完成后,关闭当前 Codex 会话,再打开一个新会话。
75
+ Skill 通常在 Agent 会话启动时被发现。安装完成后,关闭当前会话,再打开一个新会话。
70
76
 
71
- ### 4. 把板卡和目标告诉 Codex
77
+ ### 4. 把板卡和目标告诉 Agent
72
78
 
73
79
  把你已经掌握的信息填进下面的提示词即可,不用先查齐所有硬件参数。先指定工作区根目录,本地路径尽量相对工作区填写;不确定的内容写“未知”。可直接复制的版本见[开发板接入提示词](skills/tirtc-esp32-builder/assets/developer-intake-prompt.md)。
74
80
 
75
81
  ```text
76
- 请使用 $tirtc-esp32-builder 完成这块开发板的 TiRTC 移植。
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`,由它安装 Codex Skill、下载 Device Kit,并在需要时安装 ESP-IDF。npm 包没有 `preinstall`、`install` 或 `postinstall` 生命周期脚本,因此不会在安装 CLI 时改动这些目录。
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 已经由团队统一准备,可以只复制 Codex Skill:
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
- | Codex Skill | `${CODEX_HOME:-~/.codex}/skills/tirtc-esp32-builder` |
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
 
@@ -759,15 +793,16 @@ npx --yes tirtc-device-builder@latest setup esp32 --install \
759
793
 
760
794
  如果指定目录已存在但内容不完整或版本错误,安装器会拒绝覆盖。换一个空的新路径,或先人工备份原目录。
761
795
 
762
- ### 安装完成后 Codex 找不到 Skill
796
+ ### 安装完成后 Agent 客户端找不到 Skill
763
797
 
764
- 关闭当前 Codex 会话并重新打开。再检查默认文件是否存在:
798
+ 关闭当前 Agent 会话并重新打开。先查看客户端目录,再检查对应文件是否存在:
765
799
 
766
800
  ```bash
767
- ls -l ~/.codex/skills/tirtc-esp32-builder/SKILL.md
801
+ npx --yes tirtc-device-builder@latest clients
802
+ ls -l <skills-dir>/tirtc-esp32-builder/SKILL.md
768
803
  ```
769
804
 
770
- 设置过 `CODEX_HOME` 或 `--skills-dir` 时,要检查对应目录。不要同时把 Skill 安装到多个位置。
805
+ 确认安装和启动的是同一个客户端,例如 Qwen Code 要使用 `--client qwen-code`。设置过 `CODEX_HOME`、`XDG_CONFIG_HOME` 或 `--skills-dir` 时,要检查对应目录。项目级目录由客户端自身规则决定,可以用 `--skills-dir` 显式指定。
771
806
 
772
807
  ### 已有 Skill,安装器拒绝覆盖
773
808
 
@@ -926,8 +961,8 @@ metadata 中的版本、标签、上游 commit 和期望 SHA-256 必须与本地
926
961
 
927
962
  ```bash
928
963
  npm test
929
- git tag -a v0.8.1 -m "v0.8.1"
930
- git push origin v0.8.1
964
+ git tag -a v0.9.0 -m "v0.9.0"
965
+ git push origin v0.9.0
931
966
  ```
932
967
 
933
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
+ }
@@ -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 a path`);
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: defaults.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
- if (!options.skillsDir) {
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> Codex skills directory
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 Codex Skill. It does not run sudo or edit shell profiles.`);
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
- "Codex Skill",
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 Codex Skill ${runtime.packageVersion} already exists: ${context.skillTarget}`,
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
- `Codex Skill at ${context.skillTarget} has version ${
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(` Skill: ${context.skillTarget}`);
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("Start a new Codex session and invoke $tirtc-esp32-builder.");
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 install <platform> [--skills-dir <path>] [--force]
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 ${"$"}{CODEX_HOME:-~/.codex}/skills. Existing skills are
56
- preserved unless --force is explicitly supplied. Setup checks are read-only;
57
- setup --install installs missing user-space components without running sudo.`);
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: defaultSkillsDir(),
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(`Installed ${platform.skill} ${PACKAGE.version} to ${target}`);
155
- console.log(`Start a new Codex session, then invoke $${platform.skill}.`);
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
- defaultSkillsDir: defaultSkillsDir(),
272
+ defaultClient: requireAgentClient(DEFAULT_AGENT_CLIENT),
248
273
  packageRoot: PACKAGE_ROOT,
249
274
  packageVersion: PACKAGE.version,
250
275
  platform,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tirtc-device-builder",
3
- "version": "0.8.1",
4
- "description": "Install and run TiRTC device-development skills for Codex.",
3
+ "version": "0.9.0",
4
+ "description": "Install and run TiRTC device-development Agent Skills across popular coding clients.",
5
5
  "license": "MIT",
6
6
  "author": {
7
7
  "name": "TangeAI",
@@ -30,6 +30,10 @@
30
30
  },
31
31
  "keywords": [
32
32
  "codex",
33
+ "agent-skills",
34
+ "claude-code",
35
+ "gemini-cli",
36
+ "qwen-code",
33
37
  "skill",
34
38
  "tirtc",
35
39
  "esp32",
@@ -5,14 +5,15 @@
5
5
  ```bash
6
6
  npx tirtc-device-builder@latest setup esp32
7
7
  npx tirtc-device-builder@latest setup esp32 --install
8
+ npx tirtc-device-builder@latest clients
8
9
  ```
9
10
 
10
- 第一条命令只检查;第二条命令自动安装用户目录内缺失的 Skill、带校验的 ESP32 Device Kit、ESP-IDF 5.5.4 和 ESP32-S3 工具链,最后复跑 Doctor。它不执行 `sudo`,也不修改 shell 配置。安装完成后新开 Codex 会话即可调用 Skill。
11
+ 第一条命令只检查;第二条命令自动安装用户目录内缺失的 Skill、带校验的 ESP32 Device Kit、ESP-IDF 5.5.4 和 ESP32-S3 工具链,最后复跑 Doctor。它不执行 `sudo`,也不修改 shell 配置。默认客户端是 Codex;Gemini CLI、GitHub Copilot、Qwen Code、Windsurf、Cline、Kiro、Claude Code 和 OpenCode 可通过 `--client <name>` 选择,第三条命令会列出名称和实际安装目录。安装完成后重新启动所选 Agent 客户端。
11
12
 
12
- 安装后可在 Codex 中显式调用:
13
+ 安装后可直接要求 Agent 使用该 Skill:
13
14
 
14
15
  ```text
15
- $tirtc-esp32-builder
16
+ 请使用 tirtc-esp32-builder Skill。
16
17
 
17
18
  开发板:<厂商、完整型号、PCB 版本>
18
19
  资料与实物对应:<是/否/未知>
@@ -48,24 +49,24 @@ printf '%s\n' "$TIRTC_THING_CONNECT_ROOT"
48
49
  只有板卡型号:
49
50
 
50
51
  ```text
51
- $tirtc-esp32-builder 分析 <厂商> <型号> <硬件版本>,目标是 H5 实时视频、talkback 和 AI 对讲。先输出缺失资料与能力结论。
52
+ 请使用 tirtc-esp32-builder Skill 分析 <厂商> <型号> <硬件版本>,目标是 H5 实时视频、talkback 和 AI 对讲。先输出缺失资料与能力结论。
52
53
  ```
53
54
 
54
55
  提供本地资料:
55
56
 
56
57
  ```text
57
- $tirtc-esp32-builder 使用原理图 /path/board.pdf、BSP /path/vendor-project 和一键安装的 Device Kit,为该板生成 TiRTC H5/AI/CALL/VOIP ESP-IDF 工程并编译;AI/CALL/VOIP 必须通过 AEC 门禁。
58
+ 请使用 tirtc-esp32-builder Skill、原理图 /path/board.pdf、BSP /path/vendor-project 和一键安装的 Device Kit,为该板生成 TiRTC H5/AI/CALL/VOIP ESP-IDF 工程并编译;AI/CALL/VOIP 必须通过 AEC 门禁。
58
59
  ```
59
60
 
60
61
  完整实机流程:
61
62
 
62
63
  ```text
63
- $tirtc-esp32-builder 使用 /path/hardware-ir.json 生成工程,编译后烧录到 /dev/ttyACM0,验证绑定、H5 和 AI,并生成 TIRTC_PORTING_REPORT.md。
64
+ 请使用 tirtc-esp32-builder Skill /path/hardware-ir.json 生成工程,编译后烧录到 /dev/ttyACM0,验证绑定、H5 和 AI,并生成 TIRTC_PORTING_REPORT.md。
64
65
  ```
65
66
 
66
67
  ## ESP-IDF 环境检查
67
68
 
68
- 将 `<skill-dir>` 替换为安装后的 Skill 路径,例如 `~/.codex/skills/tirtc-esp32-builder`。
69
+ 将 `<skill-dir>` 替换为 `clients` 命令显示的 Skill 根目录下的 `tirtc-esp32-builder`,例如 `~/.qwen/skills/tirtc-esp32-builder`。
69
70
 
70
71
  生成工程前检查工作区和开发环境:
71
72
 
@@ -1 +1 @@
1
- 0.8.1
1
+ 0.9.0
@@ -5,7 +5,7 @@
5
5
  ## 精简模板
6
6
 
7
7
  ```text
8
- $tirtc-esp32-builder
8
+ 请使用 tirtc-esp32-builder Skill。
9
9
 
10
10
  请为下面这块开发板完成 ThingConnect TiRTC ESP32 接入。
11
11
 
@@ -1,28 +1,30 @@
1
- $tirtc-esp32-builder
1
+ 请使用 tirtc-esp32-builder Skill。
2
2
 
3
3
  请在当前工作区完成一次立创·实战派 ESP32-S3 的 ThingConnect TiRTC clean-room L-1/L0/L1 接入验证。
4
4
 
5
5
  这是 Skill、提示词和开发板资料的独立有效性测试。允许访问外网。
6
6
 
7
- 前置条件(必须由开发者在启动本次 Codex 会话前完成,不属于本提示词内的操作):
7
+ 前置条件(必须由开发者在启动本次 Agent 会话前完成,不属于本提示词内的操作):
8
8
 
9
9
  ```bash
10
- npx --yes tirtc-device-builder@0.8.1 setup esp32 --install --force-skill
10
+ export TIRTC_AGENT_CLIENT=codex
11
+ npx --yes tirtc-device-builder@0.9.0 setup esp32 --install \
12
+ --client "$TIRTC_AGENT_CLIENT" --force-skill
11
13
  ```
12
14
 
13
- 该命令只允许在当前用户目录安装固定版本的 Skill、managed ESP32 Device Kit、ESP-IDF 和工具链;禁止 sudo、系统级包变更和修改 shell profile。安装完成后,开发者必须关闭原 Codex 会话,再从本 clean-room 工作区启动一个新会话,然后粘贴本提示词。
15
+ `TIRTC_AGENT_CLIENT` 改为 `clients` 命令列出的目标客户端名称。该命令只允许在当前用户目录安装固定版本的 Skill、managed ESP32 Device Kit、ESP-IDF 和工具链;禁止 sudo、系统级包变更和修改 shell profile。安装完成后,开发者必须关闭原 Agent 会话,再从本 clean-room 工作区启动一个新会话,然后粘贴本提示词。
14
16
 
15
17
  本轮第一步先只读运行:
16
18
 
17
19
  ```bash
18
- npx --yes tirtc-device-builder@0.8.1 --version
19
- npx --yes tirtc-device-builder@0.8.1 setup esp32
20
+ npx --yes tirtc-device-builder@0.9.0 --version
21
+ npx --yes tirtc-device-builder@0.9.0 setup esp32 --client "$TIRTC_AGENT_CLIENT"
20
22
  ```
21
23
 
22
- 必须根据命令的实际输出和本机文件确认:npm 包为 0.8.1、已安装 Skill 的 `VERSION` 为 0.8.1、所选 Device Kit 的 `manifest.json` 中 `kit_version` 为 1.1.1,并且 Doctor 对 `--expected-kit 1.1.1` 输出 `OVERALL: PASS`。Plugin manifest 不属于这种 npm 安装方式的运行时前置条件,不得把不可访问的 Plugin 版本当作阻塞项。如果版本不一致、Skill 是在当前会话启动后才安装,或环境检查未通过,停止并报告前置条件不成立;不要在当前会话中替换 Skill 后继续生成工程。
24
+ 必须根据命令的实际输出和本机文件确认:npm 包为 0.9.0、已安装 Skill 的 `VERSION` 为 0.9.0、所选 Device Kit 的 `manifest.json` 中 `kit_version` 为 1.1.1,并且 Doctor 对 `--expected-kit 1.1.1` 输出 `OVERALL: PASS`。Plugin manifest 不属于这种 npm 安装方式的运行时前置条件,不得把不可访问的 Plugin 版本当作阻塞项。如果版本不一致、Skill 是在当前会话启动后才安装,或环境检查未通过,停止并报告前置条件不成立;不要在当前会话中替换 Skill 后继续生成工程。
23
25
 
24
26
  工作区与 clean-room 边界:
25
- - 将启动 Codex 时的当前目录定义为 `WORKSPACE_ROOT`。
27
+ - 将启动 Agent 客户端时的当前目录定义为 `WORKSPACE_ROOT`。
26
28
  - 所有工程输入和输出必须位于 `WORKSPACE_ROOT`。
27
29
  - 不得读取 `WORKSPACE_ROOT` 之外的项目、兄弟目录、父目录项目、备份、旧构建、日志、报告或 artifact。
28
30
  - 如果目标输出目录已经存在,停止并报告 clean-room 条件不成立;不要读取、覆盖或删除该目录。
@@ -37,7 +37,7 @@ Add `--project <generated-project>` after generation so the doctor can compare `
37
37
 
38
38
  The automatic setup downloads a versioned minimal Kit instead of cloning the ThingConnect server repository. A managed Kit is ready only when its generator and SDK files exist and `manifest.json` declares the exact pinned `kit_version`. A stale environment or managed configuration that points at an older Kit is ignored in favor of the current versioned managed path. The setup configuration records the version read from that manifest; it never substitutes the desired version for the actual one.
39
39
 
40
- The installed Skill has its own `<skill-dir>/VERSION` marker. Setup requires it to equal the npm package version and reports both values. Replacing a missing or mismatched marker requires the explicit `--install --force-skill` flow and a new Codex session.
40
+ The installed Skill has its own `<skill-dir>/VERSION` marker. Setup requires it to equal the npm package version and reports both values. Replacing a missing or mismatched marker requires the explicit `--install --force-skill` flow and a new session in the selected Agent client.
41
41
 
42
42
  For an explicitly selected legacy workspace, omit `--expected-kit`; otherwise resolve the generation root in this order:
43
43