firmwareloop 0.6.2__tar.gz → 0.7.1__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.6.2 → firmwareloop-0.7.1}/PKG-INFO +14 -7
  2. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/README.md +12 -5
  3. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/firmwareloop.egg-info/PKG-INFO +14 -7
  4. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/firmwareloop.egg-info/requires.txt +1 -1
  5. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/pyproject.toml +2 -2
  6. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/__init__.py +1 -1
  7. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/fw_mcp_server.py +87 -34
  8. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/lib/ndtdbg.py +293 -109
  9. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/lib/serial_assistant.py +128 -16
  10. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/ndtdbg_cli.py +99 -12
  11. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/serial_cli.py +106 -16
  12. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/setup-agent-mcp.ps1 +2 -2
  13. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/LICENSE +0 -0
  14. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/firmwareloop.egg-info/SOURCES.txt +0 -0
  15. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/firmwareloop.egg-info/dependency_links.txt +0 -0
  16. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/firmwareloop.egg-info/entry_points.txt +0 -0
  17. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/firmwareloop.egg-info/top_level.txt +0 -0
  18. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/setup.cfg +0 -0
  19. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/acceptance-scenario.ps1 +0 -0
  20. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/build.ps1 +0 -0
  21. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/can.ps1 +0 -0
  22. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/check-qoder-mcp.ps1 +0 -0
  23. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/common/build-backends.psm1 +0 -0
  24. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/common/fw.psm1 +0 -0
  25. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/common/uart_probe.py +0 -0
  26. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/doctor.ps1 +0 -0
  27. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/flash.ps1 +0 -0
  28. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/instrument_cli.py +0 -0
  29. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/lib/__init__.py +0 -0
  30. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/lib/instruments.py +0 -0
  31. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/logic_capture.ps1 +0 -0
  32. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/logic_decode.ps1 +0 -0
  33. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/ndtdbg_bridge.ps1 +0 -0
  34. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/reset.ps1 +0 -0
  35. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/sigrok_csv_to_fwloop.py +0 -0
  36. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/test-backends.ps1 +0 -0
  37. {firmwareloop-0.6.2 → firmwareloop-0.7.1}/tools/test.ps1 +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: firmwareloop
3
- Version: 0.6.2
3
+ Version: 0.7.1
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
@@ -18,7 +18,7 @@ Requires-Python: >=3.10
18
18
  Description-Content-Type: text/markdown
19
19
  License-File: LICENSE
20
20
  Requires-Dist: pyvisa>=1.14.0
21
- Requires-Dist: pyvisa-py>=0.7.0
21
+ Requires-Dist: pyvisa-py>=0.7.1
22
22
  Requires-Dist: pyserial>=3.5
23
23
  Requires-Dist: pytest>=8.0.0
24
24
  Requires-Dist: pyocd>=0.36.0
@@ -148,7 +148,8 @@ fwloop ndtdebugtool "C:/Users/weilin.zhang/Desktop/zwl/NdtDebugTool-v1.0.19"
148
148
  - `fw_ndtdbg_measure` — 数据轮询采集
149
149
  - `fw_ndtdbg_read_reg` / `fw_ndtdbg_write_reg` — 任意寄存器读写(写需 `write_allowed` + `confirm` 双门控)
150
150
  - `fw_ndtdbg_capture_seq` — 寄存器序列 + Saleae 波形同窗口取证
151
- - `fw_ndtdbg_flash` — .bin/.nfw 整片擦写烧录(双门控,默认拒绝)
151
+ - `fw_ndtdbg_flash` — .bin/.nfw 整片擦写烧录(双门控,默认拒绝;擦除后判 boot、校验失败整片重写)
152
+ - `fw_ndtdbg_power_cycle` — 只做目标断电复位(不做 I2C 事务、不需双门控;设备静默时的第一步,也是抓开机日志的触发端)
152
153
 
153
154
  **前置要求:**
154
155
 
@@ -382,7 +383,7 @@ claude mcp remove --scope user agentic-hil
382
383
  ├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
383
384
  ├── demo-make/ 示例固件(Make;构建测试载体)
384
385
  ├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
385
- ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.6.2)
386
+ ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.7.1)
386
387
  ├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
387
388
  ├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
388
389
  ├── CLAUDE.md Claude Code CLI 指南
@@ -392,7 +393,7 @@ claude mcp remove --scope user agentic-hil
392
393
 
393
394
  ---
394
395
 
395
- ## 能力验证状态(v0.6.2
396
+ ## 能力验证状态(v0.7.1
396
397
 
397
398
  > 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
398
399
  > `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
@@ -400,7 +401,7 @@ claude mcp remove --scope user agentic-hil
400
401
  | 能力 | 状态 |
401
402
  |---|---|
402
403
  | 双层 MCP 服务(13 个工作流与硬件工具) | Implemented + Protocol Validated(31/31 测试通过) |
403
- | 串口助手工具族(fw_serial_* 七工具, 长驻会话/发送序列/帧解析/窗口采集) | Implemented + Simulator Validated(48 例单测)-> Real Hardware Validated 待 DUT |
404
+ | 串口助手工具族(fw_serial_* 七工具, 长驻会话/发送序列/帧解析/窗口采集) | Implemented + Simulator Validated(55 例单测: 含残留 serve 自动清理 / close 进程回收 / UTF-8 线路 / 失败落 JSONL)-> Real Hardware Validated 待 DUT |
404
405
  | 一键多 Agent 脚手架(fwloop init / fw_init_project) | Implemented + Multi-Agent Validated |
405
406
  | 自定义/私有调试工具接入 Skill(fwloop-adapter) | Implemented + Multi-Skill Validated |
406
407
  | 一键终端更新(fwloop update) | Implemented + Auto-updater Validated |
@@ -414,7 +415,9 @@ claude mcp remove --scope user agentic-hil
414
415
  | Safety Policy(limits.yaml) | Implemented(simulator 校验)-> 外置权威配置 |
415
416
  | 自动修复闭环(M6 载体) | Simulator Validated -> Real Hardware Validated 待 DUT |
416
417
  | NdtBox 数据采集/调试(fw_ndtdbg_status / fw_ndtdbg_measure, ChipSea Fxx IIC) | Implemented(无硬件 Fail-Closed 已验证)-> Real Hardware Validated 待 NdtBox |
417
- | NdtBox IIC 固件烧录(fw_ndtdbg_flash, .bin/.nfw 整片擦写, 芯海 Fxx) | Real Hardware Validated(84.87.1.1 / 84.87.1.2 真机整片擦写 verify pass, ~22s;双闸门 Fail-Closed) |
418
+ | NdtBox 目标断电复位(fw_ndtdbg_power_cycle, io10 断电复位, 无写闸门) | Implemented(单测覆盖时序/探针缺失指引)-> Real Hardware Validated NdtBox |
419
+ | NdtBox IIC 固件烧录(fw_ndtdbg_flash, .bin/.nfw 整片擦写, 芯海 Fxx) | Real Hardware Validated(84.87.1.1 / 84.87.1.2 真机整片擦写 verify pass;双闸门 Fail-Closed)+ v0.7.1 **差分 boot 判据**(真机实测: 擦除后读 0x0000 = 00 00 00 00, 旧"全 FF"判据在该 ROM 上永久误判 → 已改为"仅同一条非平凡应答才熔断", 并提供 `boot_judge: off` 关闭开关) |
420
+ | 抓开机日志(fw_serial_capture + power_cycle_off_ms, 窗口内断电复位) | Implemented(simulator 验证窗口与复位串联)-> Real Hardware Validated 待 NdtBox + DUT |
418
421
 
419
422
  ---
420
423
 
@@ -427,6 +430,10 @@ claude mcp remove --scope user agentic-hil
427
430
  | 写操作 `PERMISSION_DENIED` | 全局 `lab.yaml` 的 `ndtdbg.write_allowed` 为 `false` → 临时改 `true` 并重启 Agent(测试完建议改回 `false`) |
428
431
  | sigrok 报 `Failed to receive reply to EP1 command 0x7d` | Saleae 驱动不是 libusb-win32 → 用 Zadig 重装;或设备被 Logic 软件占用(先退出) |
429
432
  | `fw_logic_decode` 空帧 | 抓取窗口内无总线事务 → 改用 `fw_ndtdbg_capture_seq`(抓取与寄存器序列同窗口进行),不要串行先抓后写 |
433
+ | 烧录报 `BOOT_NOT_ENTERED`(擦除魔数前后 0x0000 读回**完全相同**的非平凡数据) | 差分判据认定应答者仍是运行态 App(擦除被忽略)→ 仅对目标板断电重启(勿动 BOX)后重试;反复出现时确认 `off_ms`(默认 500ms,独立看门狗芯片需真 POR)与 io10 供电复位接线。若你确认目标 ROM 的 0x0000 语义特殊,用 `boot_judge: off`(lab.yaml/CLI `--boot-judge off`/MCP `boot_judge`)关闭判据,让逐窗校验做唯一判据 |
434
+ | `fw_ndtdbg_flash` 报 `TARGET_MISMATCH: 烧录校验失败 @0x....` | 校验是**权威判据**(不是探测启发式)。信封带 `stage_timings`/`probe_before_hex`/`probe_hex`/`boot_evidence`/`first_mismatch_*`:`boot_evidence=erase_effective` 说明擦除生效 → 看失配偏移是否恒定(恒定=页写未落地);`inconclusive` 说明探测无区分度(擦除后读回全 00/全 FF)→ 需人工确认是否真进了 boot;整片重写次数由 `rewrite_times` 控制(默认 3) |
435
+ | 串口 `SERIAL_PORT_ERROR: ... PermissionError(13)` | 端口被 GUI 上位机独占(NDT Debug Tool / STDataCollection.exe、串口助手)→ 关闭占用方后 `fw_serial_open`;残留会话用 `fw_serial_close`(已自动等待/强杀进程,端口释放可验证) |
436
+ | `fw_serial_status` 的 `error_class` 为 `UART_BUSY` | 会话未打开或已被占用 → 先 `fw_serial_open`;残留会话 `fw_serial_close` 清理 |
430
437
 
431
438
  完整速查表与手工部署细节见 [`Fwloop_setup_pack/README.md`](Fwloop_setup_pack/README.md)。
432
439
 
@@ -120,7 +120,8 @@ fwloop ndtdebugtool "C:/Users/weilin.zhang/Desktop/zwl/NdtDebugTool-v1.0.19"
120
120
  - `fw_ndtdbg_measure` — 数据轮询采集
121
121
  - `fw_ndtdbg_read_reg` / `fw_ndtdbg_write_reg` — 任意寄存器读写(写需 `write_allowed` + `confirm` 双门控)
122
122
  - `fw_ndtdbg_capture_seq` — 寄存器序列 + Saleae 波形同窗口取证
123
- - `fw_ndtdbg_flash` — .bin/.nfw 整片擦写烧录(双门控,默认拒绝)
123
+ - `fw_ndtdbg_flash` — .bin/.nfw 整片擦写烧录(双门控,默认拒绝;擦除后判 boot、校验失败整片重写)
124
+ - `fw_ndtdbg_power_cycle` — 只做目标断电复位(不做 I2C 事务、不需双门控;设备静默时的第一步,也是抓开机日志的触发端)
124
125
 
125
126
  **前置要求:**
126
127
 
@@ -354,7 +355,7 @@ claude mcp remove --scope user agentic-hil
354
355
  ├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
355
356
  ├── demo-make/ 示例固件(Make;构建测试载体)
356
357
  ├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
357
- ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.6.2)
358
+ ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.7.1)
358
359
  ├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
359
360
  ├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
360
361
  ├── CLAUDE.md Claude Code CLI 指南
@@ -364,7 +365,7 @@ claude mcp remove --scope user agentic-hil
364
365
 
365
366
  ---
366
367
 
367
- ## 能力验证状态(v0.6.2
368
+ ## 能力验证状态(v0.7.1
368
369
 
369
370
  > 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
370
371
  > `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
@@ -372,7 +373,7 @@ claude mcp remove --scope user agentic-hil
372
373
  | 能力 | 状态 |
373
374
  |---|---|
374
375
  | 双层 MCP 服务(13 个工作流与硬件工具) | Implemented + Protocol Validated(31/31 测试通过) |
375
- | 串口助手工具族(fw_serial_* 七工具, 长驻会话/发送序列/帧解析/窗口采集) | Implemented + Simulator Validated(48 例单测)-> Real Hardware Validated 待 DUT |
376
+ | 串口助手工具族(fw_serial_* 七工具, 长驻会话/发送序列/帧解析/窗口采集) | Implemented + Simulator Validated(55 例单测: 含残留 serve 自动清理 / close 进程回收 / UTF-8 线路 / 失败落 JSONL)-> Real Hardware Validated 待 DUT |
376
377
  | 一键多 Agent 脚手架(fwloop init / fw_init_project) | Implemented + Multi-Agent Validated |
377
378
  | 自定义/私有调试工具接入 Skill(fwloop-adapter) | Implemented + Multi-Skill Validated |
378
379
  | 一键终端更新(fwloop update) | Implemented + Auto-updater Validated |
@@ -386,7 +387,9 @@ claude mcp remove --scope user agentic-hil
386
387
  | Safety Policy(limits.yaml) | Implemented(simulator 校验)-> 外置权威配置 |
387
388
  | 自动修复闭环(M6 载体) | Simulator Validated -> Real Hardware Validated 待 DUT |
388
389
  | NdtBox 数据采集/调试(fw_ndtdbg_status / fw_ndtdbg_measure, ChipSea Fxx IIC) | Implemented(无硬件 Fail-Closed 已验证)-> Real Hardware Validated 待 NdtBox |
389
- | NdtBox IIC 固件烧录(fw_ndtdbg_flash, .bin/.nfw 整片擦写, 芯海 Fxx) | Real Hardware Validated(84.87.1.1 / 84.87.1.2 真机整片擦写 verify pass, ~22s;双闸门 Fail-Closed) |
390
+ | NdtBox 目标断电复位(fw_ndtdbg_power_cycle, io10 断电复位, 无写闸门) | Implemented(单测覆盖时序/探针缺失指引)-> Real Hardware Validated NdtBox |
391
+ | NdtBox IIC 固件烧录(fw_ndtdbg_flash, .bin/.nfw 整片擦写, 芯海 Fxx) | Real Hardware Validated(84.87.1.1 / 84.87.1.2 真机整片擦写 verify pass;双闸门 Fail-Closed)+ v0.7.1 **差分 boot 判据**(真机实测: 擦除后读 0x0000 = 00 00 00 00, 旧"全 FF"判据在该 ROM 上永久误判 → 已改为"仅同一条非平凡应答才熔断", 并提供 `boot_judge: off` 关闭开关) |
392
+ | 抓开机日志(fw_serial_capture + power_cycle_off_ms, 窗口内断电复位) | Implemented(simulator 验证窗口与复位串联)-> Real Hardware Validated 待 NdtBox + DUT |
390
393
 
391
394
  ---
392
395
 
@@ -399,6 +402,10 @@ claude mcp remove --scope user agentic-hil
399
402
  | 写操作 `PERMISSION_DENIED` | 全局 `lab.yaml` 的 `ndtdbg.write_allowed` 为 `false` → 临时改 `true` 并重启 Agent(测试完建议改回 `false`) |
400
403
  | sigrok 报 `Failed to receive reply to EP1 command 0x7d` | Saleae 驱动不是 libusb-win32 → 用 Zadig 重装;或设备被 Logic 软件占用(先退出) |
401
404
  | `fw_logic_decode` 空帧 | 抓取窗口内无总线事务 → 改用 `fw_ndtdbg_capture_seq`(抓取与寄存器序列同窗口进行),不要串行先抓后写 |
405
+ | 烧录报 `BOOT_NOT_ENTERED`(擦除魔数前后 0x0000 读回**完全相同**的非平凡数据) | 差分判据认定应答者仍是运行态 App(擦除被忽略)→ 仅对目标板断电重启(勿动 BOX)后重试;反复出现时确认 `off_ms`(默认 500ms,独立看门狗芯片需真 POR)与 io10 供电复位接线。若你确认目标 ROM 的 0x0000 语义特殊,用 `boot_judge: off`(lab.yaml/CLI `--boot-judge off`/MCP `boot_judge`)关闭判据,让逐窗校验做唯一判据 |
406
+ | `fw_ndtdbg_flash` 报 `TARGET_MISMATCH: 烧录校验失败 @0x....` | 校验是**权威判据**(不是探测启发式)。信封带 `stage_timings`/`probe_before_hex`/`probe_hex`/`boot_evidence`/`first_mismatch_*`:`boot_evidence=erase_effective` 说明擦除生效 → 看失配偏移是否恒定(恒定=页写未落地);`inconclusive` 说明探测无区分度(擦除后读回全 00/全 FF)→ 需人工确认是否真进了 boot;整片重写次数由 `rewrite_times` 控制(默认 3) |
407
+ | 串口 `SERIAL_PORT_ERROR: ... PermissionError(13)` | 端口被 GUI 上位机独占(NDT Debug Tool / STDataCollection.exe、串口助手)→ 关闭占用方后 `fw_serial_open`;残留会话用 `fw_serial_close`(已自动等待/强杀进程,端口释放可验证) |
408
+ | `fw_serial_status` 的 `error_class` 为 `UART_BUSY` | 会话未打开或已被占用 → 先 `fw_serial_open`;残留会话 `fw_serial_close` 清理 |
402
409
 
403
410
  完整速查表与手工部署细节见 [`Fwloop_setup_pack/README.md`](Fwloop_setup_pack/README.md)。
404
411
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: firmwareloop
3
- Version: 0.6.2
3
+ Version: 0.7.1
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
@@ -18,7 +18,7 @@ Requires-Python: >=3.10
18
18
  Description-Content-Type: text/markdown
19
19
  License-File: LICENSE
20
20
  Requires-Dist: pyvisa>=1.14.0
21
- Requires-Dist: pyvisa-py>=0.7.0
21
+ Requires-Dist: pyvisa-py>=0.7.1
22
22
  Requires-Dist: pyserial>=3.5
23
23
  Requires-Dist: pytest>=8.0.0
24
24
  Requires-Dist: pyocd>=0.36.0
@@ -148,7 +148,8 @@ fwloop ndtdebugtool "C:/Users/weilin.zhang/Desktop/zwl/NdtDebugTool-v1.0.19"
148
148
  - `fw_ndtdbg_measure` — 数据轮询采集
149
149
  - `fw_ndtdbg_read_reg` / `fw_ndtdbg_write_reg` — 任意寄存器读写(写需 `write_allowed` + `confirm` 双门控)
150
150
  - `fw_ndtdbg_capture_seq` — 寄存器序列 + Saleae 波形同窗口取证
151
- - `fw_ndtdbg_flash` — .bin/.nfw 整片擦写烧录(双门控,默认拒绝)
151
+ - `fw_ndtdbg_flash` — .bin/.nfw 整片擦写烧录(双门控,默认拒绝;擦除后判 boot、校验失败整片重写)
152
+ - `fw_ndtdbg_power_cycle` — 只做目标断电复位(不做 I2C 事务、不需双门控;设备静默时的第一步,也是抓开机日志的触发端)
152
153
 
153
154
  **前置要求:**
154
155
 
@@ -382,7 +383,7 @@ claude mcp remove --scope user agentic-hil
382
383
  ├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
383
384
  ├── demo-make/ 示例固件(Make;构建测试载体)
384
385
  ├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
385
- ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.6.2)
386
+ ├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.7.1)
386
387
  ├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
387
388
  ├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
388
389
  ├── CLAUDE.md Claude Code CLI 指南
@@ -392,7 +393,7 @@ claude mcp remove --scope user agentic-hil
392
393
 
393
394
  ---
394
395
 
395
- ## 能力验证状态(v0.6.2
396
+ ## 能力验证状态(v0.7.1
396
397
 
397
398
  > 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
398
399
  > `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
@@ -400,7 +401,7 @@ claude mcp remove --scope user agentic-hil
400
401
  | 能力 | 状态 |
401
402
  |---|---|
402
403
  | 双层 MCP 服务(13 个工作流与硬件工具) | Implemented + Protocol Validated(31/31 测试通过) |
403
- | 串口助手工具族(fw_serial_* 七工具, 长驻会话/发送序列/帧解析/窗口采集) | Implemented + Simulator Validated(48 例单测)-> Real Hardware Validated 待 DUT |
404
+ | 串口助手工具族(fw_serial_* 七工具, 长驻会话/发送序列/帧解析/窗口采集) | Implemented + Simulator Validated(55 例单测: 含残留 serve 自动清理 / close 进程回收 / UTF-8 线路 / 失败落 JSONL)-> Real Hardware Validated 待 DUT |
404
405
  | 一键多 Agent 脚手架(fwloop init / fw_init_project) | Implemented + Multi-Agent Validated |
405
406
  | 自定义/私有调试工具接入 Skill(fwloop-adapter) | Implemented + Multi-Skill Validated |
406
407
  | 一键终端更新(fwloop update) | Implemented + Auto-updater Validated |
@@ -414,7 +415,9 @@ claude mcp remove --scope user agentic-hil
414
415
  | Safety Policy(limits.yaml) | Implemented(simulator 校验)-> 外置权威配置 |
415
416
  | 自动修复闭环(M6 载体) | Simulator Validated -> Real Hardware Validated 待 DUT |
416
417
  | NdtBox 数据采集/调试(fw_ndtdbg_status / fw_ndtdbg_measure, ChipSea Fxx IIC) | Implemented(无硬件 Fail-Closed 已验证)-> Real Hardware Validated 待 NdtBox |
417
- | NdtBox IIC 固件烧录(fw_ndtdbg_flash, .bin/.nfw 整片擦写, 芯海 Fxx) | Real Hardware Validated(84.87.1.1 / 84.87.1.2 真机整片擦写 verify pass, ~22s;双闸门 Fail-Closed) |
418
+ | NdtBox 目标断电复位(fw_ndtdbg_power_cycle, io10 断电复位, 无写闸门) | Implemented(单测覆盖时序/探针缺失指引)-> Real Hardware Validated NdtBox |
419
+ | NdtBox IIC 固件烧录(fw_ndtdbg_flash, .bin/.nfw 整片擦写, 芯海 Fxx) | Real Hardware Validated(84.87.1.1 / 84.87.1.2 真机整片擦写 verify pass;双闸门 Fail-Closed)+ v0.7.1 **差分 boot 判据**(真机实测: 擦除后读 0x0000 = 00 00 00 00, 旧"全 FF"判据在该 ROM 上永久误判 → 已改为"仅同一条非平凡应答才熔断", 并提供 `boot_judge: off` 关闭开关) |
420
+ | 抓开机日志(fw_serial_capture + power_cycle_off_ms, 窗口内断电复位) | Implemented(simulator 验证窗口与复位串联)-> Real Hardware Validated 待 NdtBox + DUT |
418
421
 
419
422
  ---
420
423
 
@@ -427,6 +430,10 @@ claude mcp remove --scope user agentic-hil
427
430
  | 写操作 `PERMISSION_DENIED` | 全局 `lab.yaml` 的 `ndtdbg.write_allowed` 为 `false` → 临时改 `true` 并重启 Agent(测试完建议改回 `false`) |
428
431
  | sigrok 报 `Failed to receive reply to EP1 command 0x7d` | Saleae 驱动不是 libusb-win32 → 用 Zadig 重装;或设备被 Logic 软件占用(先退出) |
429
432
  | `fw_logic_decode` 空帧 | 抓取窗口内无总线事务 → 改用 `fw_ndtdbg_capture_seq`(抓取与寄存器序列同窗口进行),不要串行先抓后写 |
433
+ | 烧录报 `BOOT_NOT_ENTERED`(擦除魔数前后 0x0000 读回**完全相同**的非平凡数据) | 差分判据认定应答者仍是运行态 App(擦除被忽略)→ 仅对目标板断电重启(勿动 BOX)后重试;反复出现时确认 `off_ms`(默认 500ms,独立看门狗芯片需真 POR)与 io10 供电复位接线。若你确认目标 ROM 的 0x0000 语义特殊,用 `boot_judge: off`(lab.yaml/CLI `--boot-judge off`/MCP `boot_judge`)关闭判据,让逐窗校验做唯一判据 |
434
+ | `fw_ndtdbg_flash` 报 `TARGET_MISMATCH: 烧录校验失败 @0x....` | 校验是**权威判据**(不是探测启发式)。信封带 `stage_timings`/`probe_before_hex`/`probe_hex`/`boot_evidence`/`first_mismatch_*`:`boot_evidence=erase_effective` 说明擦除生效 → 看失配偏移是否恒定(恒定=页写未落地);`inconclusive` 说明探测无区分度(擦除后读回全 00/全 FF)→ 需人工确认是否真进了 boot;整片重写次数由 `rewrite_times` 控制(默认 3) |
435
+ | 串口 `SERIAL_PORT_ERROR: ... PermissionError(13)` | 端口被 GUI 上位机独占(NDT Debug Tool / STDataCollection.exe、串口助手)→ 关闭占用方后 `fw_serial_open`;残留会话用 `fw_serial_close`(已自动等待/强杀进程,端口释放可验证) |
436
+ | `fw_serial_status` 的 `error_class` 为 `UART_BUSY` | 会话未打开或已被占用 → 先 `fw_serial_open`;残留会话 `fw_serial_close` 清理 |
430
437
 
431
438
  完整速查表与手工部署细节见 [`Fwloop_setup_pack/README.md`](Fwloop_setup_pack/README.md)。
432
439
 
@@ -1,5 +1,5 @@
1
1
  pyvisa>=1.14.0
2
- pyvisa-py>=0.7.0
2
+ pyvisa-py>=0.7.1
3
3
  pyserial>=3.5
4
4
  pytest>=8.0.0
5
5
  pyocd>=0.36.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "firmwareloop"
7
- version = "0.6.2"
7
+ version = "0.7.1"
8
8
  description = "AI Agent Firmware Development and Lab Automation Platform (Dual-Tier MCP Server)"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -14,7 +14,7 @@ license = { text = "MIT" }
14
14
  requires-python = ">=3.10"
15
15
  dependencies = [
16
16
  "pyvisa>=1.14.0",
17
- "pyvisa-py>=0.7.0",
17
+ "pyvisa-py>=0.7.1",
18
18
  "pyserial>=3.5",
19
19
  "pytest>=8.0.0",
20
20
  "pyocd>=0.36.0",
@@ -2,4 +2,4 @@
2
2
  FirmwareLoop - AI Agent Firmware Development and Lab Automation Platform.
3
3
  """
4
4
 
5
- __version__ = "0.6.2"
5
+ __version__ = "0.7.1"
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
- fw_mcp_server.py - FirmwareLoop High-Level Workflow & Device MCP Server (v0.6.2).
3
+ fw_mcp_server.py - FirmwareLoop High-Level Workflow & Device MCP Server (v0.7.1).
4
4
 
5
5
  Exposes high-level firmware engineering and hardware management tools to AI Coding
6
6
  Agents (Antigravity CLI, Claude Code CLI, Qoder IDE, CodeBuddy IDE, Cursor) via the Model Context
@@ -21,6 +21,7 @@ Tools provided:
21
21
  - fw_ndtdbg_capture_seq: NdtBox register sequence + concurrent I2C capture for waveform evidence (ndtdbg_cli.py)
22
22
  - fw_ndtdbg_stream_start/status/stop: job mode for long stream captures (immediate job_id, CSV-tail polling, graceful stop)
23
23
  - fw_ndtdbg_flash: NdtBox IIC firmware flash (.bin/.nfw, full-chip erase, double-gated) (ndtdbg_cli.py)
24
+ - fw_ndtdbg_power_cycle: NdtBox target power-cycle only (no I2C transaction, no write gate; boot-log trigger) (ndtdbg_cli.py)
24
25
  - fw_ndtdbg_stream: NdtBox high-rate register stream capture to CSV via one long-lived USB session (read-only) (ndtdbg_cli.py)
25
26
  - fw_serial_status: Serial assistant session probe (serve liveness, port, RX/TX counters, log path) (serial_cli.py)
26
27
  - fw_serial_open: Serial assistant - open (or reuse) the persistent COM session (HEX/ASCII duplex, session log) (serial_cli.py)
@@ -331,35 +332,43 @@ def handle_fw_ndtdbg_write_reg(args: Dict[str, Any]) -> Dict[str, Any]:
331
332
  def handle_fw_ndtdbg_flash(args: Dict[str, Any]) -> Dict[str, Any]:
332
333
  """Erase + flash a .bin/.nfw image onto the NdtBox target chip (ChipSea Fxx) over IIC.
333
334
 
334
- P1-1: retry 参数 (默认 1) —— 命中 "TARGET_MISMATCH @0x0000" (未进 boot 的特征
335
- 签名: 校验读到旧向量表) 自动重试; BOOT_NOT_ENTERED 不重试 (需人工断电, 直接给指引)。
335
+ P1-3 (0.7.1): 去掉按签名重试 —— "TARGET_MISMATCH @0x0000" 正是"根本没进 boot"的
336
+ 特征签名 (旧 boot 判据假阳的同一条错误路径), 盲目重试只会重复同一失败路径。
337
+ 整片重写 (<= rewrite_times) 现由 flash_exec 内部完成, 结果带 rewrite_count;
338
+ BOOT_NOT_ENTERED 直接返回指引 (需断电重启, 重试无意义)。
336
339
  """
337
340
  cli_script = os.path.join(REPO_ROOT, "tools", "ndtdbg_cli.py")
338
341
  cmd = [PYTHON_EXE, cli_script, "flash-bin", "--file", str(args.get("file_path")),
339
342
  "--erase-ms", str(int(args.get("erase_ms", 2000)))]
343
+ if args.get("off_ms") is not None:
344
+ cmd.extend(["--off-ms", str(int(args["off_ms"]))])
345
+ if args.get("rewrite_times") is not None:
346
+ cmd.extend(["--rewrite-times", str(max(0, int(args["rewrite_times"])))])
347
+ if args.get("boot_judge"):
348
+ cmd.extend(["--boot-judge", str(args["boot_judge"])])
340
349
  if args.get("confirm"):
341
350
  cmd.append("--confirm")
342
351
  _ndtdbg_reg_args(args, cmd)
343
352
  timeout_s = min(max(int(args.get("timeout_seconds", 300)), 30), 900)
344
- retry = max(0, int(args.get("retry", 1)))
345
- result: Dict[str, Any] = {}
346
- for attempt in range(retry + 1):
347
- result = _ndtdbg_run(cmd, timeout_s)
348
- data = result.get("data") if isinstance(result.get("data"), dict) else None
349
- if not data:
350
- return result # CLI JSON body: 原样返回 (兼容 mock/异常输出)
351
- if data.get("ok"):
352
- if attempt:
353
- data["retry_count"] = attempt
354
- return data
355
- err_text = f"{data.get('error', '')} {data.get('detail', '')}"
356
- err_cls = data.get("error_class")
357
- if err_cls == "BOOT_NOT_ENTERED":
358
- return data # 需人工断电重启, 重试无意义
359
- if err_cls == "TARGET_MISMATCH" and "@0x0000" in err_text and attempt < retry:
360
- continue # 未进 boot 特征签名: 自动重试
361
- return data
362
- return result
353
+ result = _ndtdbg_run(cmd, timeout_s)
354
+ data = result.get("data") if isinstance(result.get("data"), dict) else None
355
+ return data if data else result # 无 CLI JSON body: 原样返回 (兼容 mock/异常输出)
356
+
357
+
358
+ def handle_fw_ndtdbg_power_cycle(args: Dict[str, Any]) -> Dict[str, Any]:
359
+ """P0-2: 只做一次断电复位 (usb2io IO10 off -> on), 不做任何 I2C 事务。
360
+
361
+ 设备静默时最安全的第一步 (读风险等级: 不需要 lab.yaml write_allowed), 也是
362
+ "复位 + 抓开机日志" 的触发端 —— 与 fw_serial_open/capture 配合使用。
363
+ 返回 {off_ms, powered_on, io_ms}。
364
+ """
365
+ cli_script = os.path.join(REPO_ROOT, "tools", "ndtdbg_cli.py")
366
+ cmd = [PYTHON_EXE, cli_script, "power-cycle"]
367
+ if args.get("off_ms") is not None:
368
+ cmd.extend(["--off-ms", str(int(args["off_ms"]))])
369
+ _ndtdbg_reg_args(args, cmd)
370
+ res = _ndtdbg_run(cmd, 60)
371
+ return res.get("data") or res
363
372
 
364
373
 
365
374
  def _stream_cli_cmd(args: Dict[str, Any], out_path: str, duration_cap: float) -> Optional[list]:
@@ -1378,13 +1387,22 @@ def handle_fw_serial_parse(args: Dict[str, Any]) -> Dict[str, Any]:
1378
1387
 
1379
1388
 
1380
1389
  def handle_fw_serial_capture(args: Dict[str, Any]) -> Dict[str, Any]:
1390
+ """P2-4: 可选 power_cycle_off_ms —— 开机打印只在复位瞬间出现, 必须先开端口再复位。
1391
+
1392
+ 实现: capture 窗口在后台线程先行打开 (~200ms), 窗口内触发一次断电复位
1393
+ (ndtdbg power-cycle), 于是复位瞬间及其后的启动日志都落在 CSV 里。
1394
+ """
1381
1395
  duration_ms = int(args.get("duration_ms", 3000))
1396
+ power_cycle = args.get("power_cycle_off_ms")
1382
1397
  cmd = [PYTHON_EXE, SERIAL_CLI, "capture", "--duration-ms", str(duration_ms)]
1383
1398
  if args.get("out"):
1384
1399
  cmd.extend(["--out", str(args["out"])])
1400
+ if power_cycle is not None:
1401
+ cmd.extend(["--power-cycle-off-ms", str(int(power_cycle))])
1385
1402
  if args.get("lab_config"):
1386
1403
  cmd.extend(["--lab-config", str(args["lab_config"])])
1387
- res = _serial_run(cmd, duration_ms // 1000 + 60)
1404
+ # 断电复位 = 额外一次 32 位桥进程往返 (秒级), 计入超时预算
1405
+ res = _serial_run(cmd, duration_ms // 1000 + 60 + (30 if power_cycle is not None else 0))
1388
1406
  return res.get("data") or res
1389
1407
 
1390
1408
 
@@ -1754,7 +1772,7 @@ TOOLS_REGISTRY = {
1754
1772
  "handler": handle_fw_ndtdbg_write_reg,
1755
1773
  },
1756
1774
  "fw_ndtdbg_flash": {
1757
- "description": "Erase and flash a .bin/.nfw firmware image onto the NdtBox target chip (ChipSea Fxx) over IIC via NdtDebugTool (power-cycle reset into boot -> erase magic AA 55 A5 5A -> 128B page writes -> 256B read-back verify -> finish 'PASs'). DESTRUCTIVE full-chip erase: requires lab.yaml ndtdbg.write_allowed: true AND confirm: true, otherwise PERMISSION_DENIED.",
1775
+ "description": "Erase and flash a .bin/.nfw firmware image onto the NdtBox target chip (ChipSea Fxx) over IIC via NdtDebugTool (power-cycle reset into boot -> erase magic AA 55 A5 5A -> differential BOOT JUDGEMENT: 0x0000 is sampled once BEFORE the power cycle and once AFTER the erase; only an identical NON-TRIVIAL reading (the same App answering before and after a command only the boot ROM executes) raises BOOT_NOT_ENTERED - all-00/all-FF/no-ACK are inconclusive and proceed, because a real ROM may answer 0x0000 with 00 00 00 00 after erase -> 128B page writes -> 256B read-back verify = the authoritative criterion, whole-chip rewrite on mismatch -> finish 'PASs'). DESTRUCTIVE full-chip erase: requires lab.yaml ndtdbg.write_allowed: true AND confirm: true, otherwise PERMISSION_DENIED.",
1758
1776
  "inputSchema": {
1759
1777
  "type": "object",
1760
1778
  "properties": {
@@ -1777,10 +1795,18 @@ TOOLS_REGISTRY = {
1777
1795
  "description": "CLI process timeout; budget ~30s per 64KB image (page writes paced at 30ms)",
1778
1796
  "default": 300,
1779
1797
  },
1780
- "retry": {
1798
+ "off_ms": {
1781
1799
  "type": "integer",
1782
- "description": "Auto-retries on TARGET_MISMATCH @0x0000 signature (target did not enter boot; verify read old vector table). BOOT_NOT_ENTERED never retries (manual power-cycle required). Result carries retry_count when a retry succeeded",
1783
- "default": 1,
1800
+ "description": "Power-off duration of the reset-into-boot power cycle, in ms. Default from lab.yaml ndtdbg.flash.off_ms, else 500 (200ms may only sag the rail - independent-watchdog chips such as CSA37F72 IWDG_S need a real POR)",
1801
+ },
1802
+ "rewrite_times": {
1803
+ "type": "integer",
1804
+ "description": "Whole-chip rewrite budget when read-back verify mismatches (aligned with ChipSea.cs REWRITE_TIMES). Default 3; 0 disables rewriting. The result carries rewrite_count/attempts, and a failed flash reports stage_timings/probe_before_hex/probe_hex/boot_evidence/first_mismatch_* for post-mortem",
1805
+ },
1806
+ "boot_judge": {
1807
+ "type": "string",
1808
+ "enum": ["diff", "off"],
1809
+ "description": "Boot-probe criterion. 'diff' (default): compare 0x0000 before the power cycle vs after the erase, and raise BOOT_NOT_ENTERED only when both reads are identical NON-TRIVIAL data (proof the same running App answered the erase magic). 'off': only record probe evidence (probe_before_hex/probe_hex/boot_evidence) and never block - the 256B read-back verify stays the sole criterion, i.e. exactly the bridge/GUI behaviour. Use 'off' if a target ROM reports an unexpected 0x0000 signature",
1784
1810
  },
1785
1811
  "dll_dir": {
1786
1812
  "type": "string",
@@ -1799,6 +1825,29 @@ TOOLS_REGISTRY = {
1799
1825
  },
1800
1826
  "handler": handle_fw_ndtdbg_flash,
1801
1827
  },
1828
+ "fw_ndtdbg_power_cycle": {
1829
+ "description": "Power-cycle the NdtBox target through the reset rail (usb2io IO10 off -> on, default 500ms) WITHOUT any I2C transaction. Read-risk level: lab.yaml ndtdbg.write_allowed is NOT required. Use it as the first step when the target is silent, and as the trigger for capturing boot-time UART logs (fw_serial_open + fw_serial_capture). Returns {off_ms, powered_on, io_ms}.",
1830
+ "inputSchema": {
1831
+ "type": "object",
1832
+ "properties": {
1833
+ "off_ms": {
1834
+ "type": "integer",
1835
+ "description": "Power-off duration in ms; default from lab.yaml ndtdbg.flash.off_ms, else 500. Independent-watchdog chips (e.g. CSA37F72 IWDG_S) only clear on a real POR/BOR/pin reset",
1836
+ "default": 500,
1837
+ },
1838
+ "dll_dir": {
1839
+ "type": "string",
1840
+ "description": "NdtDebugTool lib directory containing USBIOX.DLL and usb2io.dll; default from lab.yaml ndtdbg.dll_dir",
1841
+ },
1842
+ "slave_addr": {
1843
+ "type": "string",
1844
+ "description": "IIC slave address (unused by power-cycle; accepted for tool-family symmetry)",
1845
+ },
1846
+ },
1847
+ "additionalProperties": False,
1848
+ },
1849
+ "handler": handle_fw_ndtdbg_power_cycle,
1850
+ },
1802
1851
  "fw_ndtdbg_stream": {
1803
1852
  "description": "Stream-read a set of IIC registers from the NdtBox target chip (ChipSea Fxx) at a fixed interval via one long-lived USB session. Writes CSV with timestamps; returns JSON summary (frames, FF_GAP/IO_FAIL/HANG counts, interval stats, io_overhead, write stats). Protocol profiles: continuous (pure-read), request-response (pre_write), stress (interleave_writes, needs confirm). NOTE: server-side timeout = duration_s + 120s; for durations beyond ~90s the MCP client may time out first - use shorter windows or the job pattern.",
1804
1853
  "inputSchema": {
@@ -2193,7 +2242,7 @@ TOOLS_REGISTRY = {
2193
2242
  "handler": handle_fw_serial_parse,
2194
2243
  },
2195
2244
  "fw_serial_capture": {
2196
- "description": "Serial assistant: synchronous window capture - record the RX stream for duration_ms into a timestamped CSV (t_rel_ms, dir, hex, printable) for offline analysis/plotting (real-time-curve equivalent; plotting happens in your tools/Agent from the CSV). duration_ms <= 90000; long background captures (job mode) are a planned follow-up.",
2245
+ "description": "Serial assistant: synchronous window capture - record the RX stream for duration_ms into a timestamped CSV (t_rel_ms, dir, hex, printable) for offline analysis/plotting (real-time-curve equivalent; plotting happens in your tools/Agent from the CSV). Pass power_cycle_off_ms to fire an NdtBox power-cycle inside the window and capture boot-time prints (port is opened first). duration_ms <= 90000; long background captures (job mode) are a planned follow-up.",
2197
2246
  "inputSchema": {
2198
2247
  "type": "object",
2199
2248
  "properties": {
@@ -2202,6 +2251,10 @@ TOOLS_REGISTRY = {
2202
2251
  "description": "Capture window length in ms (1..90000)",
2203
2252
  "default": 3000,
2204
2253
  },
2254
+ "power_cycle_off_ms": {
2255
+ "type": "integer",
2256
+ "description": "When set, power-cycle the NdtBox target inside the capture window (off for N ms, default 500 behaviour when the value is omitted) so boot-time prints land in the CSV. The COM port is opened first, then the reset fires ~200ms into the window. Requires the NdtBox reset rail (usb2io IO10); no write_allowed needed",
2257
+ },
2205
2258
  "out": {"type": "string", "description": "CSV output path (defaults to a temp-dir file)"},
2206
2259
  "lab_config": {"type": "string", "description": "Explicit lab.yaml path override"},
2207
2260
  },
@@ -2254,7 +2307,7 @@ def process_request(req: Dict[str, Any]) -> Optional[Dict[str, Any]]:
2254
2307
  },
2255
2308
  "serverInfo": {
2256
2309
  "name": "firmwareloop",
2257
- "version": "0.6.2"
2310
+ "version": "0.7.1"
2258
2311
  }
2259
2312
  }
2260
2313
  }
@@ -2389,7 +2442,7 @@ def _settle_stashed_entrypoints(stashed) -> None:
2389
2442
  def handle_cli_update() -> int:
2390
2443
  """Handle `firmwareloop update` command."""
2391
2444
  print("============================================================")
2392
- print(" FirmwareLoop Auto-Updater (v0.6.2)")
2445
+ print(" FirmwareLoop Auto-Updater (v0.7.1)")
2393
2446
  print("============================================================")
2394
2447
 
2395
2448
  is_git_repo = os.path.exists(os.path.join(REPO_ROOT, ".git"))
@@ -2820,7 +2873,7 @@ def set_ndtdbg_dlldir(dll_path: str) -> str:
2820
2873
  def handle_cli_setup() -> int:
2821
2874
  """Handle `firmwareloop setup` command: simultaneously configure Antigravity CLI, Claude Code CLI, Qoder IDE, and CodeBuddy IDE."""
2822
2875
  print("============================================================")
2823
- print(" FirmwareLoop Multi-Agent Global Setup Helper (v0.6.2)")
2876
+ print(" FirmwareLoop Multi-Agent Global Setup Helper (v0.7.1)")
2824
2877
  print("============================================================")
2825
2878
  print("")
2826
2879
 
@@ -2945,7 +2998,7 @@ def handle_cli_setup() -> int:
2945
2998
  def handle_cli_init(chip: Optional[str] = None, upgrade: bool = False) -> int:
2946
2999
  """Handle `firmwareloop init [target_chip]` command."""
2947
3000
  print("============================================================")
2948
- print(" FirmwareLoop Multi-Agent Project Initializer (v0.6.2)")
3001
+ print(" FirmwareLoop Multi-Agent Project Initializer (v0.7.1)")
2949
3002
  print("============================================================")
2950
3003
  cwd = os.getcwd()
2951
3004
  print(f"[*] Initializing multi-agent guidelines & bench config in:\n {cwd}\n")
@@ -2978,7 +3031,7 @@ def handle_cli_init(chip: Optional[str] = None, upgrade: bool = False) -> int:
2978
3031
 
2979
3032
 
2980
3033
  def print_cli_help() -> None:
2981
- print("""FirmwareLoop (fwloop) — AI Agent Firmware Engineering & Lab Automation Platform (v0.6.2)
3034
+ print("""FirmwareLoop (fwloop) — AI Agent Firmware Engineering & Lab Automation Platform (v0.7.1)
2982
3035
 
2983
3036
  Usage:
2984
3037
  fwloop [command] (or: firmwareloop [command])
@@ -3024,7 +3077,7 @@ def main() -> None:
3024
3077
  sys.exit(2)
3025
3078
  sys.exit(handle_cli_ndtdebugtool(args[1]))
3026
3079
  elif cmd in ["version", "-v", "--version"]:
3027
- print("FirmwareLoop v0.6.2")
3080
+ print("FirmwareLoop v0.7.1")
3028
3081
  sys.exit(0)
3029
3082
  elif cmd in ["help", "-h", "--help"]:
3030
3083
  print_cli_help()