firmwareloop 0.7.4__tar.gz → 0.8.0__tar.gz

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.
Files changed (37) hide show
  1. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/PKG-INFO +6 -3
  2. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/README.md +5 -2
  3. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/firmwareloop.egg-info/PKG-INFO +6 -3
  4. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/pyproject.toml +1 -1
  5. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/__init__.py +1 -1
  6. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/fw_mcp_server.py +281 -13
  7. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/setup-agent-mcp.ps1 +2 -2
  8. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/LICENSE +0 -0
  9. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/firmwareloop.egg-info/SOURCES.txt +0 -0
  10. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/firmwareloop.egg-info/dependency_links.txt +0 -0
  11. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/firmwareloop.egg-info/entry_points.txt +0 -0
  12. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/firmwareloop.egg-info/requires.txt +0 -0
  13. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/firmwareloop.egg-info/top_level.txt +0 -0
  14. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/setup.cfg +0 -0
  15. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/acceptance-scenario.ps1 +0 -0
  16. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/build.ps1 +0 -0
  17. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/can.ps1 +0 -0
  18. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/check-qoder-mcp.ps1 +0 -0
  19. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/common/build-backends.psm1 +0 -0
  20. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/common/fw.psm1 +0 -0
  21. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/common/uart_probe.py +0 -0
  22. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/doctor.ps1 +0 -0
  23. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/flash.ps1 +0 -0
  24. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/instrument_cli.py +0 -0
  25. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/lib/__init__.py +0 -0
  26. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/lib/instruments.py +0 -0
  27. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/lib/ndtdbg.py +0 -0
  28. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/lib/serial_assistant.py +0 -0
  29. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/logic_capture.ps1 +0 -0
  30. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/logic_decode.ps1 +0 -0
  31. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/ndtdbg_bridge.ps1 +0 -0
  32. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/ndtdbg_cli.py +0 -0
  33. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/reset.ps1 +0 -0
  34. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/serial_cli.py +0 -0
  35. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/sigrok_csv_to_fwloop.py +0 -0
  36. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/test-backends.ps1 +0 -0
  37. {firmwareloop-0.7.4 → firmwareloop-0.8.0}/tools/test.ps1 +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: firmwareloop
3
- Version: 0.7.4
3
+ Version: 0.8.0
4
4
  Summary: AI Agent Firmware Development and Lab Automation Platform (Dual-Tier MCP Server)
5
5
  Author-email: WeilinZhang <19523330249@163.com>
6
6
  License: MIT
@@ -177,6 +177,8 @@ fwloop setup
177
177
  | Claude Code CLI | `~/.claude.json` |
178
178
  | Qoder / Qoder CN | `~/.qoder/mcp.json`、`~/.qoder-cn/mcp.json` |
179
179
  | CodeBuddy IDE | `~/.codebuddy/mcp.json` |
180
+ | DeepSeek Harness (DSH) | `~/.dsh/profiles/<已派生 profile>/cordis.patch.yml` |
181
+ | ZCode IDE | `~/.zcode/cli/config.json` |
180
182
 
181
183
  2. **同步技能包**(`firmwareloop`、`fwloop-adapter`)到 Antigravity 与 Claude Code 的全局 Skill 目录。
182
184
  3. **生成机器级全局配置** `%APPDATA%\FirmwareLoop\lab.yaml`(NdtBox 配置模板,已存在则不覆盖)。
@@ -383,7 +385,7 @@ claude mcp remove --scope user agentic-hil
383
385
  ├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
384
386
  ├── demo-make/ 示例固件(Make;构建测试载体)
385
387
  ├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
386
- ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.7.4)
388
+ ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.8.0)
387
389
  ├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
388
390
  ├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
389
391
  ├── CLAUDE.md Claude Code CLI 指南
@@ -393,7 +395,7 @@ claude mcp remove --scope user agentic-hil
393
395
 
394
396
  ---
395
397
 
396
- ## 能力验证状态(v0.7.4
398
+ ## 能力验证状态(v0.8.0
397
399
 
398
400
  > 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
399
401
  > `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
@@ -401,6 +403,7 @@ claude mcp remove --scope user agentic-hil
401
403
  | 能力 | 状态 |
402
404
  |---|---|
403
405
  | 双层 MCP 服务(13 个工作流与硬件工具) | Implemented + Protocol Validated(31/31 测试通过) |
406
+ | fwloop setup 一键注册(7 Agent:Antigravity / Claude Code / Qoder(+CN) / CodeBuddy / DeepSeek Harness / ZCode) | Implemented + Multi-Agent Validated(DSH / ZCode 真机写入验证) |
404
407
  | 串口助手工具族(fw_serial_* 七工具, 长驻会话/发送序列/帧解析/窗口采集) | Implemented + Simulator Validated(55 例单测: 含残留 serve 自动清理 / close 进程回收 / UTF-8 线路 / 失败落 JSONL)-> Real Hardware Validated 待 DUT |
405
408
  | 一键多 Agent 脚手架(fwloop init / fw_init_project) | Implemented + Multi-Agent Validated |
406
409
  | 自定义/私有调试工具接入 Skill(fwloop-adapter) | Implemented + Multi-Skill Validated |
@@ -149,6 +149,8 @@ fwloop setup
149
149
  | Claude Code CLI | `~/.claude.json` |
150
150
  | Qoder / Qoder CN | `~/.qoder/mcp.json`、`~/.qoder-cn/mcp.json` |
151
151
  | CodeBuddy IDE | `~/.codebuddy/mcp.json` |
152
+ | DeepSeek Harness (DSH) | `~/.dsh/profiles/<已派生 profile>/cordis.patch.yml` |
153
+ | ZCode IDE | `~/.zcode/cli/config.json` |
152
154
 
153
155
  2. **同步技能包**(`firmwareloop`、`fwloop-adapter`)到 Antigravity 与 Claude Code 的全局 Skill 目录。
154
156
  3. **生成机器级全局配置** `%APPDATA%\FirmwareLoop\lab.yaml`(NdtBox 配置模板,已存在则不覆盖)。
@@ -355,7 +357,7 @@ claude mcp remove --scope user agentic-hil
355
357
  ├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
356
358
  ├── demo-make/ 示例固件(Make;构建测试载体)
357
359
  ├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
358
- ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.7.4)
360
+ ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.8.0)
359
361
  ├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
360
362
  ├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
361
363
  ├── CLAUDE.md Claude Code CLI 指南
@@ -365,7 +367,7 @@ claude mcp remove --scope user agentic-hil
365
367
 
366
368
  ---
367
369
 
368
- ## 能力验证状态(v0.7.4
370
+ ## 能力验证状态(v0.8.0
369
371
 
370
372
  > 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
371
373
  > `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
@@ -373,6 +375,7 @@ claude mcp remove --scope user agentic-hil
373
375
  | 能力 | 状态 |
374
376
  |---|---|
375
377
  | 双层 MCP 服务(13 个工作流与硬件工具) | Implemented + Protocol Validated(31/31 测试通过) |
378
+ | fwloop setup 一键注册(7 Agent:Antigravity / Claude Code / Qoder(+CN) / CodeBuddy / DeepSeek Harness / ZCode) | Implemented + Multi-Agent Validated(DSH / ZCode 真机写入验证) |
376
379
  | 串口助手工具族(fw_serial_* 七工具, 长驻会话/发送序列/帧解析/窗口采集) | Implemented + Simulator Validated(55 例单测: 含残留 serve 自动清理 / close 进程回收 / UTF-8 线路 / 失败落 JSONL)-> Real Hardware Validated 待 DUT |
377
380
  | 一键多 Agent 脚手架(fwloop init / fw_init_project) | Implemented + Multi-Agent Validated |
378
381
  | 自定义/私有调试工具接入 Skill(fwloop-adapter) | Implemented + Multi-Skill Validated |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: firmwareloop
3
- Version: 0.7.4
3
+ Version: 0.8.0
4
4
  Summary: AI Agent Firmware Development and Lab Automation Platform (Dual-Tier MCP Server)
5
5
  Author-email: WeilinZhang <19523330249@163.com>
6
6
  License: MIT
@@ -177,6 +177,8 @@ fwloop setup
177
177
  | Claude Code CLI | `~/.claude.json` |
178
178
  | Qoder / Qoder CN | `~/.qoder/mcp.json`、`~/.qoder-cn/mcp.json` |
179
179
  | CodeBuddy IDE | `~/.codebuddy/mcp.json` |
180
+ | DeepSeek Harness (DSH) | `~/.dsh/profiles/<已派生 profile>/cordis.patch.yml` |
181
+ | ZCode IDE | `~/.zcode/cli/config.json` |
180
182
 
181
183
  2. **同步技能包**(`firmwareloop`、`fwloop-adapter`)到 Antigravity 与 Claude Code 的全局 Skill 目录。
182
184
  3. **生成机器级全局配置** `%APPDATA%\FirmwareLoop\lab.yaml`(NdtBox 配置模板,已存在则不覆盖)。
@@ -383,7 +385,7 @@ claude mcp remove --scope user agentic-hil
383
385
  ├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
384
386
  ├── demo-make/ 示例固件(Make;构建测试载体)
385
387
  ├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
386
- ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.7.4)
388
+ ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.8.0)
387
389
  ├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
388
390
  ├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
389
391
  ├── CLAUDE.md Claude Code CLI 指南
@@ -393,7 +395,7 @@ claude mcp remove --scope user agentic-hil
393
395
 
394
396
  ---
395
397
 
396
- ## 能力验证状态(v0.7.4
398
+ ## 能力验证状态(v0.8.0
397
399
 
398
400
  > 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
399
401
  > `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
@@ -401,6 +403,7 @@ claude mcp remove --scope user agentic-hil
401
403
  | 能力 | 状态 |
402
404
  |---|---|
403
405
  | 双层 MCP 服务(13 个工作流与硬件工具) | Implemented + Protocol Validated(31/31 测试通过) |
406
+ | fwloop setup 一键注册(7 Agent:Antigravity / Claude Code / Qoder(+CN) / CodeBuddy / DeepSeek Harness / ZCode) | Implemented + Multi-Agent Validated(DSH / ZCode 真机写入验证) |
404
407
  | 串口助手工具族(fw_serial_* 七工具, 长驻会话/发送序列/帧解析/窗口采集) | Implemented + Simulator Validated(55 例单测: 含残留 serve 自动清理 / close 进程回收 / UTF-8 线路 / 失败落 JSONL)-> Real Hardware Validated 待 DUT |
405
408
  | 一键多 Agent 脚手架(fwloop init / fw_init_project) | Implemented + Multi-Agent Validated |
406
409
  | 自定义/私有调试工具接入 Skill(fwloop-adapter) | Implemented + Multi-Skill Validated |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "firmwareloop"
7
- version = "0.7.4"
7
+ version = "0.8.0"
8
8
  description = "AI Agent Firmware Development and Lab Automation Platform (Dual-Tier MCP Server)"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -2,4 +2,4 @@
2
2
  FirmwareLoop - AI Agent Firmware Development and Lab Automation Platform.
3
3
  """
4
4
 
5
- __version__ = "0.7.4"
5
+ __version__ = "0.8.0"
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
- fw_mcp_server.py - FirmwareLoop High-Level Workflow & Device MCP Server (v0.7.4).
3
+ fw_mcp_server.py - FirmwareLoop High-Level Workflow & Device MCP Server (v0.8.0).
4
4
 
5
5
  Exposes high-level firmware engineering and hardware management tools to AI Coding
6
- Agents (Antigravity CLI, Claude Code CLI, Qoder IDE, CodeBuddy IDE, Cursor) via the Model Context
6
+ Agents (Antigravity CLI, Claude Code CLI, Qoder IDE, CodeBuddy IDE, DeepSeek Harness,
7
+ ZCode IDE, Cursor) via the Model Context
7
8
  Protocol (JSON-RPC 2.0 stdio).
8
9
 
9
10
  Tools provided:
@@ -1156,6 +1157,57 @@ custom_tools:
1156
1157
  向用户展示配置位置,并说明后续在对话中可以直接使用自然语言调度:
1157
1158
  - “*使用我们公司的烧录工具把固件刷入芯片*”
1158
1159
  - “*启动自研抓包工具采集 5 秒串口日志*”
1160
+
1161
+ ---
1162
+
1163
+ ## 可选手法:目标侧"只读诊断插桩"(通用模式)
1164
+
1165
+ **适用**:目标固件"挂死 / 反复重启",没有可用串口输出(或写通道不可用),但工具族能读寄存器。
1166
+ 思路:让固件把**运行痕迹暴露成只读寄存器**,Agent 一次读回即可定位阶段与崩溃现场,
1167
+ 不必"改一处 → 烧一次 → 猜一次"。
1168
+
1169
+ > **⚠️ 归属规则(先看这条)**:本 skill 会**随包分发**(`fwloop init` / `fwloop setup` 会把它
1170
+ > 复制到用户工程与各 Agent 的 skills 目录),因此**这里只放与具体项目、具体芯片无关的通用内容**。
1171
+ > 每个项目的**地址约定、寄存器含义、现场案例属于项目资产**,请记录到:
1172
+ > - 该项目仓库内的 `docs/<project>-probe-convention.md`,或该项目自建的 project skill;
1173
+ >
1174
+ > 本 skill 负责教你**怎么设计**这套插桩、以及**记录到哪里**,不承载任何单一项目的约定。
1175
+
1176
+ ### 通用模式(由粗到细四件套)
1177
+
1178
+ 1. **阶段编号**:最后到达的启动边界;
1179
+ 2. **阶段追踪环**:最近 N 步的环形缓冲(看清"路径",而不只是终点);
1180
+ 3. **计数器组**:主循环迭代数、可疑等待循环的自旋计数、崩溃现场(fault 状态字 / PC / LR / xPSR);
1181
+ 4. **运行时可调"停靠级别"**:写后断电即可逐级二分崩溃点。
1182
+
1183
+ ### 通用约束(与芯片无关,都是踩出来的)
1184
+
1185
+ 1. 插桩数组放 **NOINIT** 段:复位/软复位不清零,才看得到"重启前最后执行到哪";
1186
+ 2. 尽量**只读注册**,不要占用可写寄存器语义;
1187
+ 3. 注册表必须按**地址升序**:乱序会导致从机自检失败 / 初始化提前 `return`
1188
+ ⇒ 通信通道根本没起来,现象与"目标掉电"完全一样(极易误判);
1189
+ 4. **避开厂商协议占用的地址**:例——烧录工具会往某个固定地址写擦除/完成魔数,
1190
+ 插桩变量放在那里会污染工具的判据;
1191
+ 5. 采集遵守工具族的协议约束(升序读、单次长度上限、关键字节双读校验——见各工具描述);
1192
+ 6. 插桩代码用 `#if` 开关包裹:关闭时零成本、零 RAM。
1193
+
1194
+ ### 固件侧两条"已知良方"(与芯片无关)
1195
+
1196
+ 1. **"通道先行"**:把"能被工具读到的通道"(I2C 从机 / 串口)初始化提到所有可疑初始化**之前**
1197
+ ⇒ 之后无论哪里崩,设备仍然可读(把它写在最后,往往会被插桩本身锁死);
1198
+ 2. **fault handler 禁止裸自旋**(`B .`):其优先级高于一切可配置中断,死等会饿死通信中断,
1199
+ 目标"从总线上消失"(工具侧表现为 `BUS_TIMEOUT`)⇒ 应"记录现场 + 复位"。
1200
+ (进阶手法"改写异常返回地址 → 恢复函数 → 重建通道 → 停靠"与芯片强相关,换目标需复测。)
1201
+
1202
+ ### 最小闭环(通用)
1203
+
1204
+ ```text
1205
+ fw_ndtdbg_power_cycle(off_ms=500) # 复位(不需 write_allowed)
1206
+ fw_ndtdbg_status # 分级结论 + 判据表
1207
+ fw_ndtdbg_read_reg(reg=..., len=...) # 读"项目约定文档"里定义的诊断寄存器
1208
+ fw_ndtdbg_flash(...) # 改完固件自助烧录(含烧后冷启动冒烟)
1209
+ fw_serial_capture(power_cycle_off_ms=500) # 抓开机日志(窗口内自动复位)
1210
+ ```
1159
1211
  """
1160
1212
  if not os.path.exists(adapter_skill_path) or overwrite:
1161
1213
  with open(adapter_skill_path, "w", encoding="utf-8") as fh:
@@ -2335,7 +2387,7 @@ def process_request(req: Dict[str, Any]) -> Optional[Dict[str, Any]]:
2335
2387
  },
2336
2388
  "serverInfo": {
2337
2389
  "name": "firmwareloop",
2338
- "version": "0.7.4"
2390
+ "version": "0.8.0"
2339
2391
  }
2340
2392
  }
2341
2393
  }
@@ -2470,7 +2522,7 @@ def _settle_stashed_entrypoints(stashed) -> None:
2470
2522
  def handle_cli_update() -> int:
2471
2523
  """Handle `firmwareloop update` command."""
2472
2524
  print("============================================================")
2473
- print(" FirmwareLoop Auto-Updater (v0.7.4)")
2525
+ print(" FirmwareLoop Auto-Updater (v0.8.0)")
2474
2526
  print("============================================================")
2475
2527
 
2476
2528
  is_git_repo = os.path.exists(os.path.join(REPO_ROOT, ".git"))
@@ -2813,6 +2865,192 @@ def configure_codebuddy_user_mcp(user_home: str) -> Dict[str, Any]:
2813
2865
  return {"ok": False, "path": mcp_file, "reason": str(exc)}
2814
2866
 
2815
2867
 
2868
+ DSH_MCP_CLIENT_PLUGIN = "@deepseek-ai/dsh-mcp-client"
2869
+
2870
+ # DSH (DeepSeek Harness) profile patch entries. Tools surface as
2871
+ # mcp__<serverName>__<tool> (same shape as Claude Code / Codex), e.g.
2872
+ # mcp__fwloop__fw_build. Bare command names resolve via PATH: the DSH
2873
+ # stdio transport only scrubs credential-shaped / DSH_* env vars, so the
2874
+ # parent PATH survives (same convention as the Qoder / Claude entries).
2875
+ DSH_MCP_SERVER_ENTRIES = (
2876
+ ("mcp-fwloop", "fwloop", {"command": "fwloop", "args": []}),
2877
+ ("mcp-agentic-hil", "agentic-hil", {"command": "agentic-hil", "args": ["mcp-stdio"]}),
2878
+ )
2879
+
2880
+ DSH_PATCH_HEADER = (
2881
+ "# FirmwareLoop dual-tier MCP (added by fwloop setup).\n"
2882
+ "# Bridged by @deepseek-ai/dsh-mcp-client; tools surface as mcp__<serverName>__<tool>.\n"
2883
+ )
2884
+
2885
+
2886
+ def resolve_dsh_home(user_home: str) -> str:
2887
+ """Resolve the DeepSeek Harness home: non-blank $DSH_HOME wins, else ~/.dsh.
2888
+
2889
+ Mirrors @deepseek-ai/dsh-home-paths precedence (an in-harness configured
2890
+ home override is invisible to external tools and therefore not honored).
2891
+ """
2892
+ env_home = (os.environ.get("DSH_HOME") or "").strip()
2893
+ return env_home if env_home else os.path.join(user_home, ".dsh")
2894
+
2895
+
2896
+ def discover_dsh_profiles(dsh_home: str) -> List[str]:
2897
+ """List derived DSH profile names under <home>/profiles.
2898
+
2899
+ A directory counts as a profile only when it has a profile-root cordis.yml,
2900
+ which excludes the shared <home>/profiles/node_modules dependency tree.
2901
+ Built-in profiles (web/tui/headless) appear here once the user has booted
2902
+ or derived them; never-derived ones do not exist on disk and are skipped.
2903
+ """
2904
+ profiles_dir = os.path.join(dsh_home, "profiles")
2905
+ found: List[str] = []
2906
+ if os.path.isdir(profiles_dir):
2907
+ for name in sorted(os.listdir(profiles_dir)):
2908
+ cand = os.path.join(profiles_dir, name)
2909
+ if os.path.isdir(cand) and os.path.isfile(os.path.join(cand, "cordis.yml")):
2910
+ found.append(name)
2911
+ return found
2912
+
2913
+
2914
+ def _dsh_entry_yaml(entry_id: str, server_name: str, spec: Dict[str, Any]) -> str:
2915
+ args_repr = ", ".join(f"'{a}'" for a in spec.get("args", []))
2916
+ return (
2917
+ f" - id: {entry_id}\n"
2918
+ f" name: '{DSH_MCP_CLIENT_PLUGIN}'\n"
2919
+ f" config:\n"
2920
+ f" serverName: {server_name}\n"
2921
+ f" transport: stdio\n"
2922
+ f" command: {spec['command']}\n"
2923
+ f" args: [{args_repr}]\n"
2924
+ )
2925
+
2926
+
2927
+ def upsert_dsh_patch_file(patch_path: str) -> str:
2928
+ """Idempotently add the FirmwareLoop MCP entries to a profile's cordis.patch.yml.
2929
+
2930
+ Returns "written" or "already_configured". Text-level upsert on purpose:
2931
+ a yaml.safe_dump round-trip would strip the user's guide comments, so an
2932
+ existing file is only ever appended to. Membership is decided by a plain
2933
+ substring check on our fixed ids (fail-safe side: a false positive merely
2934
+ skips the write), and entries that already exist are never rewritten so
2935
+ user overrides (e.g. a custom command) survive setup re-runs.
2936
+ """
2937
+ present = ""
2938
+ if os.path.exists(patch_path):
2939
+ with open(patch_path, "r", encoding="utf-8-sig") as f:
2940
+ present = f.read()
2941
+ missing = [eid for eid, _, _ in DSH_MCP_SERVER_ENTRIES if f"id: {eid}" not in present]
2942
+ if present and not missing:
2943
+ return "already_configured"
2944
+ # Single "- insert:" block holding the missing entries, matching the shape
2945
+ # of the official dsh-mcp-client sample (one block, several entries).
2946
+ new_text = "- insert:\n" + "".join(
2947
+ _dsh_entry_yaml(eid, name, spec)
2948
+ for eid, name, spec in DSH_MCP_SERVER_ENTRIES
2949
+ if eid in missing
2950
+ )
2951
+ parent = os.path.dirname(patch_path)
2952
+ if parent:
2953
+ os.makedirs(parent, exist_ok=True)
2954
+ if not present:
2955
+ with open(patch_path, "w", encoding="utf-8", newline="\n") as f:
2956
+ f.write(DSH_PATCH_HEADER)
2957
+ f.write(new_text)
2958
+ return "written"
2959
+ stripped = present.strip()
2960
+ with open(patch_path, "w", encoding="utf-8", newline="\n") as f:
2961
+ if not stripped or stripped == "[]":
2962
+ # Empty document (blank or literal []): keep leading comment lines,
2963
+ # then start the entries array.
2964
+ comments = "\n".join(
2965
+ line for line in present.splitlines() if line.strip().startswith("#")
2966
+ )
2967
+ if comments:
2968
+ f.write(comments + "\n")
2969
+ f.write(new_text)
2970
+ else:
2971
+ f.write(present)
2972
+ if not present.endswith("\n"):
2973
+ f.write("\n")
2974
+ f.write("\n" + new_text)
2975
+ return "written"
2976
+
2977
+
2978
+ def configure_dsh_profiles(user_home: str) -> List[Dict[str, Any]]:
2979
+ """Add FirmwareLoop MCP entries to every derived DSH profile's cordis.patch.yml."""
2980
+ dsh_home = resolve_dsh_home(user_home)
2981
+ results: List[Dict[str, Any]] = []
2982
+ for profile in discover_dsh_profiles(dsh_home):
2983
+ patch_path = os.path.join(dsh_home, "profiles", profile, "cordis.patch.yml")
2984
+ try:
2985
+ status = upsert_dsh_patch_file(patch_path)
2986
+ results.append({"profile": profile, "ok": True, "path": patch_path, "status": status})
2987
+ except Exception as exc:
2988
+ results.append({"profile": profile, "ok": False, "path": patch_path, "reason": str(exc)})
2989
+ return results
2990
+
2991
+
2992
+ ZCODE_MCP_SERVERS = {
2993
+ "fwloop": {"command": "fwloop", "type": "stdio"},
2994
+ "agentic-hil": {"command": "agentic-hil", "args": ["mcp-stdio"], "type": "stdio"},
2995
+ }
2996
+
2997
+
2998
+ def upsert_zcode_mcp_json(mcp_path: str, servers: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
2999
+ """Merge MCP servers into ZCode's ~/.zcode/cli/config.json without dropping
3000
+ existing entries.
3001
+
3002
+ ZCode (GLM desktop IDE) nests its MCP registry under "mcp"."servers" —
3003
+ the per-entry shape (type/command/args) matches Qoder's mcpServers, so the
3004
+ same per-entry merge (keep user extras like env) applies.
3005
+ """
3006
+ payload = dict(servers or ZCODE_MCP_SERVERS)
3007
+ data: Dict[str, Any] = {}
3008
+ if os.path.exists(mcp_path):
3009
+ try:
3010
+ with open(mcp_path, "r", encoding="utf-8-sig") as f:
3011
+ loaded = json.load(f)
3012
+ if isinstance(loaded, dict):
3013
+ data = loaded
3014
+ except Exception:
3015
+ data = {}
3016
+ mcp_root = data.get("mcp")
3017
+ if not isinstance(mcp_root, dict):
3018
+ mcp_root = {}
3019
+ mcp_servers = mcp_root.get("servers")
3020
+ if not isinstance(mcp_servers, dict):
3021
+ mcp_servers = {}
3022
+ for name, entry in payload.items():
3023
+ old = mcp_servers.get(name)
3024
+ if isinstance(old, dict) and isinstance(entry, dict):
3025
+ merged = dict(old)
3026
+ merged.update(entry)
3027
+ mcp_servers[name] = merged
3028
+ else:
3029
+ mcp_servers[name] = entry
3030
+ mcp_root["servers"] = mcp_servers
3031
+ data["mcp"] = mcp_root
3032
+ parent = os.path.dirname(mcp_path)
3033
+ if parent:
3034
+ os.makedirs(parent, exist_ok=True)
3035
+ with open(mcp_path, "w", encoding="utf-8") as f:
3036
+ json.dump(data, f, indent=2)
3037
+ f.write("\n")
3038
+ return data
3039
+
3040
+
3041
+ def configure_zcode_user_mcp(user_home: str) -> Dict[str, Any]:
3042
+ """Write FirmwareLoop MCP entries into ZCode's ~/.zcode/cli/config.json."""
3043
+ zcode_dir = os.path.join(user_home, ".zcode")
3044
+ mcp_file = os.path.join(zcode_dir, "cli", "config.json")
3045
+ if not os.path.isdir(zcode_dir):
3046
+ return {"ok": False, "path": mcp_file, "reason": "not_installed"}
3047
+ try:
3048
+ upsert_zcode_mcp_json(mcp_file)
3049
+ return {"ok": True, "path": mcp_file}
3050
+ except Exception as exc:
3051
+ return {"ok": False, "path": mcp_file, "reason": str(exc)}
3052
+
3053
+
2816
3054
  def resolve_ndtdebugtool_lib(tool_root: str) -> str:
2817
3055
  """Resolve the NdtDebugTool lib directory (must contain USBIOX.DLL)."""
2818
3056
  if not tool_root or not os.path.isdir(tool_root):
@@ -2899,9 +3137,9 @@ def set_ndtdbg_dlldir(dll_path: str) -> str:
2899
3137
 
2900
3138
 
2901
3139
  def handle_cli_setup() -> int:
2902
- """Handle `firmwareloop setup` command: simultaneously configure Antigravity CLI, Claude Code CLI, Qoder IDE, and CodeBuddy IDE."""
3140
+ """Handle `firmwareloop setup` command: simultaneously configure Antigravity CLI, Claude Code CLI, Qoder IDE, CodeBuddy IDE, DeepSeek Harness (DSH), and ZCode IDE."""
2903
3141
  print("============================================================")
2904
- print(" FirmwareLoop Multi-Agent Global Setup Helper (v0.7.4)")
3142
+ print(" FirmwareLoop Multi-Agent Global Setup Helper (v0.8.0)")
2905
3143
  print("============================================================")
2906
3144
  print("")
2907
3145
 
@@ -2985,7 +3223,37 @@ def handle_cli_setup() -> int:
2985
3223
  else:
2986
3224
  print(f" - User MCP: [Warning] Could not write {cb_result['path']}: {cb_result.get('reason')}")
2987
3225
 
2988
- # 5. Global Skills Sync for Antigravity and Claude Code
3226
+ # 5. DeepSeek Harness (DSH) per-profile cordis.patch.yml upsert
3227
+ # Only profiles already derived under <home>/profiles (a booted or
3228
+ # --from-default-profile'd profile has a profile-root cordis.yml) are
3229
+ # configured; never-derived built-ins do not exist on disk and are skipped.
3230
+ print("\n[5] DeepSeek Harness (DSH):")
3231
+ dsh_results = configure_dsh_profiles(user_home)
3232
+ dsh_ok = [item for item in dsh_results if item.get("ok")]
3233
+ if dsh_ok:
3234
+ for item in dsh_ok:
3235
+ state = "already configured" if item["status"] == "already_configured" else "written"
3236
+ print(f" - profile {item['profile']}: [OK] {state}: {item['path']}")
3237
+ configured_agents.append("DeepSeek Harness (DSH)")
3238
+ else:
3239
+ print(" - ~/.dsh/profiles has no derived profile (DSH not installed, or no profile booted yet)")
3240
+ print(" - Boot/derive a profile in DSH first, then re-run: fwloop setup")
3241
+ for item in dsh_results:
3242
+ if not item.get("ok"):
3243
+ print(f" - profile {item['profile']}: [Warning] Could not write {item['path']}: {item.get('reason')}")
3244
+
3245
+ # 6. ZCode IDE user-level MCP (~/.zcode/cli/config.json, {"mcp": {"servers": ...}})
3246
+ print("\n[6] ZCode IDE:")
3247
+ zc_result = configure_zcode_user_mcp(user_home)
3248
+ if zc_result.get("ok"):
3249
+ print(f" - User MCP: [OK] Written to {zc_result['path']}")
3250
+ configured_agents.append("ZCode")
3251
+ elif zc_result.get("reason") == "not_installed":
3252
+ print(" - User MCP: ~/.zcode not found (Workspace .mcp.json still supported)")
3253
+ else:
3254
+ print(f" - User MCP: [Warning] Could not write {zc_result['path']}: {zc_result.get('reason')}")
3255
+
3256
+ # 7. Global Skills Sync for Antigravity and Claude Code
2989
3257
  ag_skills_dir = os.path.join(user_home, ".gemini", "antigravity-cli", "skills")
2990
3258
  claude_skills_dir = os.path.join(user_home, ".claude", "skills")
2991
3259
  source_skills_dir = os.path.join(REPO_ROOT, "skills")
@@ -3006,14 +3274,14 @@ def handle_cli_setup() -> int:
3006
3274
  except Exception:
3007
3275
  pass
3008
3276
 
3009
- print(f"\n[5] Global Skills:")
3277
+ print(f"\n[7] Global Skills:")
3010
3278
  print(f" - Synchronized: [OK] {skills_synced} skills (firmwareloop, fwloop-adapter) to Antigravity & Claude Code")
3011
3279
 
3012
- # 5. Machine-level global lab.yaml (ndtdbg config for the globally installed tool)
3280
+ # 8. Machine-level global lab.yaml (ndtdbg config for the globally installed tool)
3013
3281
  global_cfg = ensure_global_lab_yaml()
3014
3282
  if global_cfg:
3015
3283
  exists = " [已存在, 未覆盖]" if os.path.isfile(global_cfg) else " [已创建]"
3016
- print("\n[6] NdtBox 全局配置:")
3284
+ print("\n[8] NdtBox 全局配置:")
3017
3285
  print(f" - {global_cfg}{exists}")
3018
3286
  print(" - 若未配置, 请运行: fwloop ndtdebugtool \"<NdtDebugTool 根目录>\"")
3019
3287
 
@@ -3026,7 +3294,7 @@ def handle_cli_setup() -> int:
3026
3294
  def handle_cli_init(chip: Optional[str] = None, upgrade: bool = False) -> int:
3027
3295
  """Handle `firmwareloop init [target_chip]` command."""
3028
3296
  print("============================================================")
3029
- print(" FirmwareLoop Multi-Agent Project Initializer (v0.7.4)")
3297
+ print(" FirmwareLoop Multi-Agent Project Initializer (v0.8.0)")
3030
3298
  print("============================================================")
3031
3299
  cwd = os.getcwd()
3032
3300
  print(f"[*] Initializing multi-agent guidelines & bench config in:\n {cwd}\n")
@@ -3059,7 +3327,7 @@ def handle_cli_init(chip: Optional[str] = None, upgrade: bool = False) -> int:
3059
3327
 
3060
3328
 
3061
3329
  def print_cli_help() -> None:
3062
- print("""FirmwareLoop (fwloop) — AI Agent Firmware Engineering & Lab Automation Platform (v0.7.4)
3330
+ print("""FirmwareLoop (fwloop) — AI Agent Firmware Engineering & Lab Automation Platform (v0.8.0)
3063
3331
 
3064
3332
  Usage:
3065
3333
  fwloop [command] (or: firmwareloop [command])
@@ -3105,7 +3373,7 @@ def main() -> None:
3105
3373
  sys.exit(2)
3106
3374
  sys.exit(handle_cli_ndtdebugtool(args[1]))
3107
3375
  elif cmd in ["version", "-v", "--version"]:
3108
- print("FirmwareLoop v0.7.4")
3376
+ print("FirmwareLoop v0.8.0")
3109
3377
  sys.exit(0)
3110
3378
  elif cmd in ["help", "-h", "--help"]:
3111
3379
  print_cli_help()
@@ -1,6 +1,6 @@
1
1
  <#
2
2
  .SYNOPSIS
3
- Dual-Tier MCP setup and registration helper (v0.7.4).
3
+ Dual-Tier MCP setup and registration helper (v0.8.0).
4
4
  Inspects installed AI coding agents (Qoder, Claude Code, Antigravity, Cursor)
5
5
  and configures global MCP / skills or prints exact registration commands.
6
6
 
@@ -242,7 +242,7 @@ if ($Json) {
242
242
  Write-FwJson ([pscustomobject]$report)
243
243
  } else {
244
244
  Write-Host "============================================================" -ForegroundColor Cyan
245
- Write-Host " FirmwareLoop Dual-Tier MCP & Agent Setup Helper (v0.7.4)" -ForegroundColor Green
245
+ Write-Host " FirmwareLoop Dual-Tier MCP & Agent Setup Helper (v0.8.0)" -ForegroundColor Green
246
246
  Write-Host "============================================================" -ForegroundColor Cyan
247
247
  Write-Host ""
248
248
  Write-Host "[1] Google Antigravity / Gemini CLI:" -ForegroundColor Yellow
File without changes
File without changes
File without changes