firmwareloop 0.8.0__tar.gz → 0.9.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.
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/PKG-INFO +6 -4
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/README.md +3 -3
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/firmwareloop.egg-info/PKG-INFO +6 -4
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/firmwareloop.egg-info/SOURCES.txt +4 -1
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/firmwareloop.egg-info/requires.txt +3 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/pyproject.toml +12 -1
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/__init__.py +1 -1
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/fw_mcp_server.py +149 -80
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/lib/ndtdbg.py +63 -29
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/ndtdbg_cli.py +33 -8
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/setup-agent-mcp.ps1 +2 -2
- firmwareloop-0.9.1/tools/templates/lab.example.yaml +195 -0
- firmwareloop-0.9.1/tools/templates/skill-firmwareloop.md +37 -0
- firmwareloop-0.9.1/tools/templates/skill-fwloop-adapter.md +190 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/LICENSE +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/firmwareloop.egg-info/dependency_links.txt +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/firmwareloop.egg-info/entry_points.txt +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/firmwareloop.egg-info/top_level.txt +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/setup.cfg +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/acceptance-scenario.ps1 +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/build.ps1 +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/can.ps1 +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/check-qoder-mcp.ps1 +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/common/build-backends.psm1 +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/common/fw.psm1 +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/common/uart_probe.py +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/doctor.ps1 +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/flash.ps1 +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/instrument_cli.py +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/lib/__init__.py +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/lib/instruments.py +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/lib/serial_assistant.py +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/logic_capture.ps1 +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/logic_decode.ps1 +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/ndtdbg_bridge.ps1 +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/reset.ps1 +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/serial_cli.py +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/sigrok_csv_to_fwloop.py +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/test-backends.ps1 +0 -0
- {firmwareloop-0.8.0 → firmwareloop-0.9.1}/tools/test.ps1 +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: firmwareloop
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.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
|
|
@@ -24,6 +24,8 @@ Requires-Dist: pytest>=8.0.0
|
|
|
24
24
|
Requires-Dist: pyocd>=0.36.0
|
|
25
25
|
Requires-Dist: pyyaml>=6.0
|
|
26
26
|
Requires-Dist: agentic-hil>=0.14.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest-xdist>=3.6.0; extra == "dev"
|
|
27
29
|
Dynamic: license-file
|
|
28
30
|
|
|
29
31
|
# FirmwareLoop (`fwloop`)
|
|
@@ -385,7 +387,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
385
387
|
├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
|
|
386
388
|
├── demo-make/ 示例固件(Make;构建测试载体)
|
|
387
389
|
├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
|
|
388
|
-
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.
|
|
390
|
+
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.9.1)
|
|
389
391
|
├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
|
|
390
392
|
├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
|
|
391
393
|
├── CLAUDE.md Claude Code CLI 指南
|
|
@@ -395,7 +397,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
395
397
|
|
|
396
398
|
---
|
|
397
399
|
|
|
398
|
-
## 能力验证状态(v0.
|
|
400
|
+
## 能力验证状态(v0.9.1)
|
|
399
401
|
|
|
400
402
|
> 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
|
|
401
403
|
> `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
|
|
@@ -419,7 +421,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
419
421
|
| 自动修复闭环(M6 载体) | Simulator Validated -> Real Hardware Validated 待 DUT |
|
|
420
422
|
| NdtBox 数据采集/调试(fw_ndtdbg_status / fw_ndtdbg_measure, ChipSea Fxx IIC) | Implemented(无硬件 Fail-Closed 已验证)-> Real Hardware Validated 待 NdtBox |
|
|
421
423
|
| NdtBox 目标断电复位(fw_ndtdbg_power_cycle, 仅 io10 断电复位, 无写闸门) | Implemented(单测覆盖时序/探针缺失指引)-> Real Hardware Validated 待 NdtBox |
|
|
422
|
-
| NdtBox IIC 固件烧录(fw_ndtdbg_flash, .bin/.nfw 整片擦写, 芯海 Fxx) | Real Hardware Validated
|
|
424
|
+
| NdtBox IIC 固件烧录(fw_ndtdbg_flash, .bin/.nfw 整片擦写, 芯海 Fxx) | Real Hardware Validated(两批真实固件镜像真机整片擦写 verify pass; 2026-09-11 同批文件桥内单体 flash 6/6 pass;双闸门 Fail-Closed)。**v0.7.2 起默认引擎 = 桥内单体 flash**(逐笔 serve 路径现场实测"擦除生效但页写不落地",保留 `flash_engine: serve` 供 A/B);差分 boot 判据 + `boot_judge: off` 逃生开关见 0.7.1;**待验收声明:默认引擎路径(Python 组 plan → Popen 桥 → 解析信封)尚待真机验收** |
|
|
423
425
|
| 烧后冷启动冒烟(fw_ndtdbg_flash 默认开, `cold_boot_check`) | Implemented(单测覆盖通过/全 FF/期望不符/关闭;`COLD_BOOT_VERIFY_FAILED` 错误类)-> Real Hardware Validated 待真机 |
|
|
424
426
|
| 串口占用取证(`port_probe` 试开硬结论 + `holder_candidates` 候选名单) | Implemented(试开结论可复现;候选按 cmdline/name 标注置信度) |
|
|
425
427
|
| 抓开机日志(fw_serial_capture + power_cycle_off_ms, 窗口内断电复位) | Implemented(simulator 验证窗口与复位串联)-> Real Hardware Validated 待 NdtBox + DUT |
|
|
@@ -357,7 +357,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
357
357
|
├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
|
|
358
358
|
├── demo-make/ 示例固件(Make;构建测试载体)
|
|
359
359
|
├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
|
|
360
|
-
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.
|
|
360
|
+
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.9.1)
|
|
361
361
|
├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
|
|
362
362
|
├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
|
|
363
363
|
├── CLAUDE.md Claude Code CLI 指南
|
|
@@ -367,7 +367,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
367
367
|
|
|
368
368
|
---
|
|
369
369
|
|
|
370
|
-
## 能力验证状态(v0.
|
|
370
|
+
## 能力验证状态(v0.9.1)
|
|
371
371
|
|
|
372
372
|
> 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
|
|
373
373
|
> `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
|
|
@@ -391,7 +391,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
391
391
|
| 自动修复闭环(M6 载体) | Simulator Validated -> Real Hardware Validated 待 DUT |
|
|
392
392
|
| NdtBox 数据采集/调试(fw_ndtdbg_status / fw_ndtdbg_measure, ChipSea Fxx IIC) | Implemented(无硬件 Fail-Closed 已验证)-> Real Hardware Validated 待 NdtBox |
|
|
393
393
|
| NdtBox 目标断电复位(fw_ndtdbg_power_cycle, 仅 io10 断电复位, 无写闸门) | Implemented(单测覆盖时序/探针缺失指引)-> Real Hardware Validated 待 NdtBox |
|
|
394
|
-
| NdtBox IIC 固件烧录(fw_ndtdbg_flash, .bin/.nfw 整片擦写, 芯海 Fxx) | Real Hardware Validated
|
|
394
|
+
| NdtBox IIC 固件烧录(fw_ndtdbg_flash, .bin/.nfw 整片擦写, 芯海 Fxx) | Real Hardware Validated(两批真实固件镜像真机整片擦写 verify pass; 2026-09-11 同批文件桥内单体 flash 6/6 pass;双闸门 Fail-Closed)。**v0.7.2 起默认引擎 = 桥内单体 flash**(逐笔 serve 路径现场实测"擦除生效但页写不落地",保留 `flash_engine: serve` 供 A/B);差分 boot 判据 + `boot_judge: off` 逃生开关见 0.7.1;**待验收声明:默认引擎路径(Python 组 plan → Popen 桥 → 解析信封)尚待真机验收** |
|
|
395
395
|
| 烧后冷启动冒烟(fw_ndtdbg_flash 默认开, `cold_boot_check`) | Implemented(单测覆盖通过/全 FF/期望不符/关闭;`COLD_BOOT_VERIFY_FAILED` 错误类)-> Real Hardware Validated 待真机 |
|
|
396
396
|
| 串口占用取证(`port_probe` 试开硬结论 + `holder_candidates` 候选名单) | Implemented(试开结论可复现;候选按 cmdline/name 标注置信度) |
|
|
397
397
|
| 抓开机日志(fw_serial_capture + power_cycle_off_ms, 窗口内断电复位) | Implemented(simulator 验证窗口与复位串联)-> Real Hardware Validated 待 NdtBox + DUT |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: firmwareloop
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.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
|
|
@@ -24,6 +24,8 @@ Requires-Dist: pytest>=8.0.0
|
|
|
24
24
|
Requires-Dist: pyocd>=0.36.0
|
|
25
25
|
Requires-Dist: pyyaml>=6.0
|
|
26
26
|
Requires-Dist: agentic-hil>=0.14.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest-xdist>=3.6.0; extra == "dev"
|
|
27
29
|
Dynamic: license-file
|
|
28
30
|
|
|
29
31
|
# FirmwareLoop (`fwloop`)
|
|
@@ -385,7 +387,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
385
387
|
├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
|
|
386
388
|
├── demo-make/ 示例固件(Make;构建测试载体)
|
|
387
389
|
├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
|
|
388
|
-
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.
|
|
390
|
+
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.9.1)
|
|
389
391
|
├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
|
|
390
392
|
├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
|
|
391
393
|
├── CLAUDE.md Claude Code CLI 指南
|
|
@@ -395,7 +397,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
395
397
|
|
|
396
398
|
---
|
|
397
399
|
|
|
398
|
-
## 能力验证状态(v0.
|
|
400
|
+
## 能力验证状态(v0.9.1)
|
|
399
401
|
|
|
400
402
|
> 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
|
|
401
403
|
> `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
|
|
@@ -419,7 +421,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
419
421
|
| 自动修复闭环(M6 载体) | Simulator Validated -> Real Hardware Validated 待 DUT |
|
|
420
422
|
| NdtBox 数据采集/调试(fw_ndtdbg_status / fw_ndtdbg_measure, ChipSea Fxx IIC) | Implemented(无硬件 Fail-Closed 已验证)-> Real Hardware Validated 待 NdtBox |
|
|
421
423
|
| NdtBox 目标断电复位(fw_ndtdbg_power_cycle, 仅 io10 断电复位, 无写闸门) | Implemented(单测覆盖时序/探针缺失指引)-> Real Hardware Validated 待 NdtBox |
|
|
422
|
-
| NdtBox IIC 固件烧录(fw_ndtdbg_flash, .bin/.nfw 整片擦写, 芯海 Fxx) | Real Hardware Validated
|
|
424
|
+
| NdtBox IIC 固件烧录(fw_ndtdbg_flash, .bin/.nfw 整片擦写, 芯海 Fxx) | Real Hardware Validated(两批真实固件镜像真机整片擦写 verify pass; 2026-09-11 同批文件桥内单体 flash 6/6 pass;双闸门 Fail-Closed)。**v0.7.2 起默认引擎 = 桥内单体 flash**(逐笔 serve 路径现场实测"擦除生效但页写不落地",保留 `flash_engine: serve` 供 A/B);差分 boot 判据 + `boot_judge: off` 逃生开关见 0.7.1;**待验收声明:默认引擎路径(Python 组 plan → Popen 桥 → 解析信封)尚待真机验收** |
|
|
423
425
|
| 烧后冷启动冒烟(fw_ndtdbg_flash 默认开, `cold_boot_check`) | Implemented(单测覆盖通过/全 FF/期望不符/关闭;`COLD_BOOT_VERIFY_FAILED` 错误类)-> Real Hardware Validated 待真机 |
|
|
424
426
|
| 串口占用取证(`port_probe` 试开硬结论 + `holder_candidates` 候选名单) | Implemented(试开结论可复现;候选按 cmdline/name 标注置信度) |
|
|
425
427
|
| 抓开机日志(fw_serial_capture + power_cycle_off_ms, 窗口内断电复位) | Implemented(simulator 验证窗口与复位串联)-> Real Hardware Validated 待 NdtBox + DUT |
|
|
@@ -32,4 +32,7 @@ tools/common/uart_probe.py
|
|
|
32
32
|
tools/lib/__init__.py
|
|
33
33
|
tools/lib/instruments.py
|
|
34
34
|
tools/lib/ndtdbg.py
|
|
35
|
-
tools/lib/serial_assistant.py
|
|
35
|
+
tools/lib/serial_assistant.py
|
|
36
|
+
tools/templates/lab.example.yaml
|
|
37
|
+
tools/templates/skill-firmwareloop.md
|
|
38
|
+
tools/templates/skill-fwloop-adapter.md
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "firmwareloop"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.9.1"
|
|
8
8
|
description = "AI Agent Firmware Development and Lab Automation Platform (Dual-Tier MCP Server)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -42,9 +42,20 @@ fwloop = "tools.fw_mcp_server:main"
|
|
|
42
42
|
fw-mcp = "tools.fw_mcp_server:main"
|
|
43
43
|
fw = "tools.fw_mcp_server:main"
|
|
44
44
|
|
|
45
|
+
# 开发/验收工具链 (不随 wheel 分发给使用者): 并行跑单测用
|
|
46
|
+
# uv pip install -e ".[dev]"
|
|
47
|
+
# python -m pytest tests/unit -n auto --dist=loadfile
|
|
48
|
+
[project.optional-dependencies]
|
|
49
|
+
dev = [
|
|
50
|
+
"pytest-xdist>=3.6.0",
|
|
51
|
+
]
|
|
52
|
+
|
|
45
53
|
[tool.setuptools.packages.find]
|
|
46
54
|
where = ["."]
|
|
47
55
|
include = ["tools*"]
|
|
48
56
|
|
|
49
57
|
[tool.setuptools.package-data]
|
|
50
58
|
"*" = ["*.ps1", "*.psm1", "*.yaml", "*.md", "*.json"]
|
|
59
|
+
# 随包分发的模板/资源 (v0.9.1): tools/templates/ 下的 lab.yaml 模板与 skill 副本。
|
|
60
|
+
# 仓库根的 lab/ 与 skills/ 不在 wheel 内, 安装态必须靠这些副本 (防漂移测试锁定一致)。
|
|
61
|
+
"tools" = ["templates/*.yaml", "templates/*.md"]
|
|
@@ -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.
|
|
3
|
+
fw_mcp_server.py - FirmwareLoop High-Level Workflow & Device MCP Server (v0.9.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, DeepSeek Harness,
|
|
@@ -384,13 +384,16 @@ def handle_fw_ndtdbg_power_cycle(args: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
384
384
|
def _stream_cli_cmd(args: Dict[str, Any], out_path: str, duration_cap: float) -> Optional[list]:
|
|
385
385
|
"""构造 stream CLI 命令 (同步/job 共用); 返回 None 表示参数非法。"""
|
|
386
386
|
cli_script = os.path.join(REPO_ROOT, "tools", "ndtdbg_cli.py")
|
|
387
|
-
regs = args.get("regs") or ["0xEE:8"]
|
|
388
387
|
duration_s = min(max(float(args.get("duration_s", 10)), 0.0), duration_cap)
|
|
389
388
|
max_frames = int(args.get("max_frames", 0))
|
|
390
389
|
if duration_s <= 0 and max_frames <= 0:
|
|
391
390
|
return None
|
|
392
|
-
cmd = [sys.executable, cli_script, "stream", "--
|
|
393
|
-
|
|
391
|
+
cmd = [sys.executable, cli_script, "stream", "--out", out_path]
|
|
392
|
+
regs = args.get("regs")
|
|
393
|
+
if regs:
|
|
394
|
+
if isinstance(regs, str):
|
|
395
|
+
regs = [regs]
|
|
396
|
+
cmd.extend(["--regs", *[str(r) for r in regs]])
|
|
394
397
|
if args.get("interval_ms") is not None:
|
|
395
398
|
cmd.extend(["--interval-ms", str(int(args["interval_ms"]))])
|
|
396
399
|
if duration_s > 0:
|
|
@@ -567,10 +570,13 @@ def handle_fw_ndtdbg_capture_seq(args: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
567
570
|
cli_script = os.path.join(REPO_ROOT, "tools", "ndtdbg_cli.py")
|
|
568
571
|
cap_script = os.path.join(REPO_ROOT, "tools", "logic_capture.ps1")
|
|
569
572
|
duration_ms = int(args.get("duration_ms", 8000))
|
|
570
|
-
writes = args.get("writes"
|
|
571
|
-
|
|
573
|
+
writes = args.get("writes")
|
|
574
|
+
if not writes:
|
|
575
|
+
return {"ok": False, "error_class": "CONFIG_ERROR",
|
|
576
|
+
"error": "fw_ndtdbg_capture_seq 必须传入非空 writes 序列 (如 [{'reg': '0xBE', 'data': '01'}]), 无默认序列; 参见目标固件协议文档"}
|
|
577
|
+
poll_reg = args.get("poll_reg")
|
|
572
578
|
poll_attempts = int(args.get("poll_attempts", 5))
|
|
573
|
-
readback_reg = args.get("readback_reg"
|
|
579
|
+
readback_reg = args.get("readback_reg")
|
|
574
580
|
poll_expect = " ".join(str(args.get("poll_expect", "00 00")).split()).upper()
|
|
575
581
|
confirm = bool(args.get("confirm", True))
|
|
576
582
|
|
|
@@ -811,13 +817,47 @@ def _detect_hardware() -> Dict[str, Any]:
|
|
|
811
817
|
return detected
|
|
812
818
|
|
|
813
819
|
|
|
820
|
+
_PACKAGE_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
821
|
+
|
|
822
|
+
|
|
823
|
+
def _lab_example_template_path() -> str:
|
|
824
|
+
"""lab.yaml 唯一模板定位 (v0.9.1 修复安装态 FileNotFoundError):
|
|
825
|
+
源码仓库态优先 lab/lab.example.yaml (开发真身); wheel 安装态
|
|
826
|
+
(REPO_ROOT=site-packages, 无 lab/ 目录) 回退随包分发的
|
|
827
|
+
tools/templates/lab.example.yaml——两份由防漂移测试逐行锁定一致。"""
|
|
828
|
+
repo = os.path.join(REPO_ROOT, "lab", "lab.example.yaml")
|
|
829
|
+
if os.path.isfile(repo):
|
|
830
|
+
return repo
|
|
831
|
+
return os.path.join(_PACKAGE_DIR, "templates", "lab.example.yaml")
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
def _skill_template_path(name: str) -> Optional[str]:
|
|
835
|
+
"""skill 源文件定位: 源码仓库 skills/<name>/SKILL.md 优先 (skills/ 不随 wheel
|
|
836
|
+
分发), 安装态回退包内 tools/templates/skill-<name>.md; 均缺失返回 None。"""
|
|
837
|
+
repo = os.path.join(REPO_ROOT, "skills", name, "SKILL.md")
|
|
838
|
+
if os.path.isfile(repo):
|
|
839
|
+
return repo
|
|
840
|
+
pkg = os.path.join(_PACKAGE_DIR, "templates", f"skill-{name}.md")
|
|
841
|
+
return pkg if os.path.isfile(pkg) else None
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
def _project_lab_yaml_path() -> str:
|
|
845
|
+
"""配置写入位置: 源码仓库态 (REPO_ROOT/lab 存在) 写仓库 lab/lab.yaml (工程习惯
|
|
846
|
+
与既有测试锚点不变); 安装态写调用方工程 CWD/lab/lab.yaml (IDE 以工程根为 CWD
|
|
847
|
+
启动 MCP), 避免把配置写进 site-packages 自身。"""
|
|
848
|
+
repo_lab = os.path.join(REPO_ROOT, "lab")
|
|
849
|
+
if os.path.isdir(repo_lab):
|
|
850
|
+
return os.path.join(repo_lab, "lab.yaml")
|
|
851
|
+
return os.path.join(os.getcwd(), "lab", "lab.yaml")
|
|
852
|
+
|
|
853
|
+
|
|
814
854
|
def _build_lab_yaml_from_example(target_dir: str, target_chip: str, build_backend: str,
|
|
815
855
|
detected_dll_dir: Optional[str]) -> str:
|
|
816
856
|
"""P1-5: lab/lab.example.yaml 为唯一模板 (example 加段 init 自动跟上);
|
|
817
857
|
仅替换 project.name/target_chip/build_backend/source_dir 与 dut.uart.port
|
|
818
858
|
(留空 + 注释 "用 fw_scan_hardware 探测", 去掉硬编码 COM3);
|
|
819
859
|
ndtdbg.dll_dir 按探测结果预填, 探测不到保持注释态 + 人工指引。"""
|
|
820
|
-
example_path =
|
|
860
|
+
example_path = _lab_example_template_path()
|
|
821
861
|
with open(example_path, encoding="utf-8") as fh:
|
|
822
862
|
lines = fh.read().splitlines()
|
|
823
863
|
out: List[str] = []
|
|
@@ -865,7 +905,7 @@ def _upgrade_lab_yaml(existing_path: str) -> List[str]:
|
|
|
865
905
|
import yaml as _yaml
|
|
866
906
|
with open(existing_path, encoding="utf-8") as fh:
|
|
867
907
|
existing = _yaml.safe_load(fh) or {}
|
|
868
|
-
example_path =
|
|
908
|
+
example_path = _lab_example_template_path()
|
|
869
909
|
with open(example_path, encoding="utf-8") as fh:
|
|
870
910
|
example = _yaml.safe_load(fh) or {}
|
|
871
911
|
added: List[str] = []
|
|
@@ -902,7 +942,7 @@ def handle_fw_init_project(args: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
902
942
|
return {
|
|
903
943
|
"ok": False,
|
|
904
944
|
"error_class": "CONFIG_ERROR",
|
|
905
|
-
"error": "target_chip 必填 (如 STM32F103C8 /
|
|
945
|
+
"error": "target_chip 必填 (如 STM32F103C8 / STM32F407ZG)",
|
|
906
946
|
"detail": "fwloop init <target_chip> 或 MCP fw_init_project(target_chip=...)",
|
|
907
947
|
}
|
|
908
948
|
|
|
@@ -1225,8 +1265,8 @@ fw_serial_capture(power_cycle_off_ms=500) # 抓开机日志(窗口内自动
|
|
|
1225
1265
|
|
|
1226
1266
|
|
|
1227
1267
|
def handle_fw_configure_lab(args: Dict[str, Any]) -> Dict[str, Any]:
|
|
1228
|
-
lab_yaml_path =
|
|
1229
|
-
example_yaml_path =
|
|
1268
|
+
lab_yaml_path = _project_lab_yaml_path()
|
|
1269
|
+
example_yaml_path = _lab_example_template_path()
|
|
1230
1270
|
|
|
1231
1271
|
config: Dict[str, Any] = {}
|
|
1232
1272
|
if os.path.exists(lab_yaml_path):
|
|
@@ -1936,8 +1976,7 @@ TOOLS_REGISTRY = {
|
|
|
1936
1976
|
"regs": {
|
|
1937
1977
|
"type": "array",
|
|
1938
1978
|
"items": {"type": "string"},
|
|
1939
|
-
"description": "Register specs 'addr:len[:name]', e.g. ['0xEE:8:ch'
|
|
1940
|
-
"default": ["0xEE:8"],
|
|
1979
|
+
"description": "Register specs 'addr:len[:name]', e.g. ['0xEE:8:ch'] (EXAMPLE). Required unless configured in lab.yaml ndtdbg.stream.regs or ndtdbg.target_profile.stream_regs.",
|
|
1941
1980
|
},
|
|
1942
1981
|
"interval_ms": {
|
|
1943
1982
|
"type": "integer",
|
|
@@ -1961,7 +2000,7 @@ TOOLS_REGISTRY = {
|
|
|
1961
2000
|
"decode": {
|
|
1962
2001
|
"type": "string",
|
|
1963
2002
|
"enum": ["hex", "int16le", "uint16le", "int8", "uint8"],
|
|
1964
|
-
"description": "Optional decoded columns besides raw hex (int16le for
|
|
2003
|
+
"description": "Optional decoded columns besides raw hex (e.g. int16le for 4-channel little-endian sensor frames).",
|
|
1965
2004
|
"default": "hex",
|
|
1966
2005
|
},
|
|
1967
2006
|
"retry": {
|
|
@@ -1987,7 +2026,7 @@ TOOLS_REGISTRY = {
|
|
|
1987
2026
|
"interleave_writes": {
|
|
1988
2027
|
"type": "array",
|
|
1989
2028
|
"items": {"type": "string"},
|
|
1990
|
-
"description": "Interleave-write stress profile (P1-1): 'addr:cycle:v1,v2,...:every=N' (rotate values every N frames) or 'addr:once:v:frame=M' (single write at frame M). Written in-frame gaps over the SAME USB session (no dual-open). Requires write_allowed: true AND confirm: true. e.g. ['0x56:cycle:00,01,02,03:every=8'] reproduces
|
|
2029
|
+
"description": "Interleave-write stress profile (P1-1): 'addr:cycle:v1,v2,...:every=N' (rotate values every N frames) or 'addr:once:v:frame=M' (single write at frame M). Written in-frame gaps over the SAME USB session (no dual-open). Requires write_allowed: true AND confirm: true. e.g. ['0x56:cycle:00,01,02,03:every=8'] reproduces schedule-race style concurrent-write stress profiles (example from a real product debugging session).",
|
|
1991
2030
|
},
|
|
1992
2031
|
"confirm": {
|
|
1993
2032
|
"type": "boolean",
|
|
@@ -1997,7 +2036,6 @@ TOOLS_REGISTRY = {
|
|
|
1997
2036
|
"dll_dir": {"type": "string", "description": "NdtDebugTool lib dir; default from lab.yaml ndtdbg.dll_dir"},
|
|
1998
2037
|
"slave_addr": {"type": "string", "description": "IIC slave address, e.g. 0xA0; default from lab.yaml"},
|
|
1999
2038
|
},
|
|
2000
|
-
"required": ["regs"],
|
|
2001
2039
|
"additionalProperties": False,
|
|
2002
2040
|
},
|
|
2003
2041
|
"handler": handle_fw_ndtdbg_stream,
|
|
@@ -2010,8 +2048,7 @@ TOOLS_REGISTRY = {
|
|
|
2010
2048
|
"regs": {
|
|
2011
2049
|
"type": "array",
|
|
2012
2050
|
"items": {"type": "string"},
|
|
2013
|
-
"description": "Register specs 'addr:len[:name]'.
|
|
2014
|
-
"default": ["0xEE:8"],
|
|
2051
|
+
"description": "Register specs 'addr:len[:name]', e.g. ['0xEE:8:ch'] (EXAMPLE). Required unless configured in lab.yaml ndtdbg.stream.regs or ndtdbg.target_profile.stream_regs.",
|
|
2015
2052
|
},
|
|
2016
2053
|
"interval_ms": {"type": "integer", "description": "Target sampling interval in ms.", "default": 100},
|
|
2017
2054
|
"duration_s": {"type": "number", "description": "Total capture duration in seconds (cap 3600).", "default": 10},
|
|
@@ -2056,7 +2093,7 @@ TOOLS_REGISTRY = {
|
|
|
2056
2093
|
"handler": handle_fw_ndtdbg_stream_stop,
|
|
2057
2094
|
},
|
|
2058
2095
|
"fw_ndtdbg_capture_seq": {
|
|
2059
|
-
"description": "One-call protocol evidence: concurrently runs a
|
|
2096
|
+
"description": "One-call protocol evidence: concurrently runs a target-specific register write sequence and a sigrok I2C capture, returning the capture path for fw_logic_decode. REQUIRED: pass your target firmware's real register protocol via writes (e.g. [{'reg': '0xBE', 'data': '01'}]). Optional poll_reg/poll_expect/readback_reg for handshake & verification. Writes are double-gated (confirm: true + lab.yaml ndtdbg.write_allowed); degrades to sequence-only execution (capture_skipped) when sigrok-cli is missing.",
|
|
2060
2097
|
"inputSchema": {
|
|
2061
2098
|
"type": "object",
|
|
2062
2099
|
"properties": {
|
|
@@ -2074,20 +2111,21 @@ TOOLS_REGISTRY = {
|
|
|
2074
2111
|
"data": {"type": "string"},
|
|
2075
2112
|
},
|
|
2076
2113
|
},
|
|
2077
|
-
"description": "
|
|
2114
|
+
"description": "Target-specific register write sequence (e.g. [{'reg': '0xBE', 'data': '01'}]). Required, no hardcoded default.",
|
|
2078
2115
|
},
|
|
2079
2116
|
"confirm": {
|
|
2080
2117
|
"type": "boolean",
|
|
2081
2118
|
"description": "Acknowledge the register write intent; handler auto-passes --confirm to the CLI (double-gated with lab.yaml ndtdbg.write_allowed)",
|
|
2082
2119
|
"default": True,
|
|
2083
2120
|
},
|
|
2084
|
-
"poll_reg": {"type": "string", "description": "
|
|
2121
|
+
"poll_reg": {"type": "string", "description": "Optional poll register until it matches poll_expect (no default; skipped if omitted)"},
|
|
2085
2122
|
"poll_expect": {"type": "string", "description": "Expected poll register value (hex) that ends the poll loop; default '00 00' - set to match your firmware protocol", "default": "00 00"},
|
|
2086
2123
|
"poll_attempts": {"type": "integer", "description": "Max poll attempts", "default": 5},
|
|
2087
|
-
"readback_reg": {"type": "string", "description": "
|
|
2124
|
+
"readback_reg": {"type": "string", "description": "Optional read-back verification register (no default; skipped if omitted)"},
|
|
2088
2125
|
"dll_dir": {"type": "string", "description": "NdtDebugTool lib directory; default from lab.yaml"},
|
|
2089
2126
|
"slave_addr": {"type": "string", "description": "IIC slave address (e.g. 0xA0)"},
|
|
2090
2127
|
},
|
|
2128
|
+
"required": ["writes"],
|
|
2091
2129
|
"additionalProperties": False,
|
|
2092
2130
|
},
|
|
2093
2131
|
"handler": handle_fw_ndtdbg_capture_seq,
|
|
@@ -2174,7 +2212,7 @@ TOOLS_REGISTRY = {
|
|
|
2174
2212
|
},
|
|
2175
2213
|
"target_chip": {
|
|
2176
2214
|
"type": "string",
|
|
2177
|
-
"description": "Target MCU chip model (e.g. STM32F103C8,
|
|
2215
|
+
"description": "Target MCU chip model (e.g. STM32F103C8, STM32F407ZG) - REQUIRED, no hardcoded default",
|
|
2178
2216
|
},
|
|
2179
2217
|
"build_backend": {
|
|
2180
2218
|
"type": "string",
|
|
@@ -2387,7 +2425,7 @@ def process_request(req: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
|
2387
2425
|
},
|
|
2388
2426
|
"serverInfo": {
|
|
2389
2427
|
"name": "firmwareloop",
|
|
2390
|
-
"version": "0.
|
|
2428
|
+
"version": "0.9.1"
|
|
2391
2429
|
}
|
|
2392
2430
|
}
|
|
2393
2431
|
}
|
|
@@ -2522,7 +2560,7 @@ def _settle_stashed_entrypoints(stashed) -> None:
|
|
|
2522
2560
|
def handle_cli_update() -> int:
|
|
2523
2561
|
"""Handle `firmwareloop update` command."""
|
|
2524
2562
|
print("============================================================")
|
|
2525
|
-
print(" FirmwareLoop Auto-Updater (v0.
|
|
2563
|
+
print(" FirmwareLoop Auto-Updater (v0.9.1)")
|
|
2526
2564
|
print("============================================================")
|
|
2527
2565
|
|
|
2528
2566
|
is_git_repo = os.path.exists(os.path.join(REPO_ROOT, ".git"))
|
|
@@ -2659,6 +2697,18 @@ def handle_cli_selftest(target: str = "ndtdbg") -> int:
|
|
|
2659
2697
|
def report(name, status, detail=""):
|
|
2660
2698
|
results.append((name, status, detail))
|
|
2661
2699
|
|
|
2700
|
+
# P2-2: 从生效配置加载产品协议 profile (避免硬编码 0xEE 与 0xBE)
|
|
2701
|
+
try:
|
|
2702
|
+
from tools import ndtdbg_cli
|
|
2703
|
+
_cfg = ndtdbg_cli.load_config(None)
|
|
2704
|
+
except Exception:
|
|
2705
|
+
_cfg = {}
|
|
2706
|
+
ndt_cfg = (_cfg.get("ndtdbg") or {}) if isinstance(_cfg, dict) else {}
|
|
2707
|
+
target_profile = ndt_cfg.get("target_profile") or {}
|
|
2708
|
+
stream_cfg = ndt_cfg.get("stream") or {}
|
|
2709
|
+
stream_regs = target_profile.get("stream_regs") or stream_cfg.get("regs")
|
|
2710
|
+
loop_cfg = target_profile.get("write_loop") or target_profile.get("selftest_sequence")
|
|
2711
|
+
|
|
2662
2712
|
# 1. status 分层探测
|
|
2663
2713
|
print("\n[1/4] status 探测 (Box / IIC bus / target chip) ...")
|
|
2664
2714
|
rc, body = run_cli("status", timeout=30)
|
|
@@ -2675,40 +2725,58 @@ def handle_cli_selftest(target: str = "ndtdbg") -> int:
|
|
|
2675
2725
|
report("protocol.desync", _st, _detail)
|
|
2676
2726
|
|
|
2677
2727
|
# 2. stream 冒烟 (100ms x 10s)
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
report("write.restore", "PASS", "0xBE=00 已恢复")
|
|
2728
|
+
if stream_regs:
|
|
2729
|
+
if isinstance(stream_regs, str):
|
|
2730
|
+
stream_regs = [stream_regs]
|
|
2731
|
+
stream_cmd = ["stream", "--interval-ms", "100", "--duration-s", "10",
|
|
2732
|
+
"--regs", *[str(r) for r in stream_regs]]
|
|
2733
|
+
frame_cfg = target_profile.get("measure_frame") or {}
|
|
2734
|
+
dec = frame_cfg.get("decode")
|
|
2735
|
+
if dec and dec != "raw":
|
|
2736
|
+
stream_cmd.extend(["--decode", dec])
|
|
2737
|
+
print(f"[2/4] stream 冒烟 (100ms x 10s, regs={stream_regs}) ...")
|
|
2738
|
+
rc, body = run_cli(*stream_cmd, timeout=60)
|
|
2739
|
+
stream_data = (body or {}).get("data") if isinstance(body, dict) else None
|
|
2740
|
+
if rc == 0 and stream_data:
|
|
2741
|
+
total = stream_data.get("frames_total", 0)
|
|
2742
|
+
conserved = (stream_data.get("frames_ok", 0) + stream_data.get("frames_ff_gap", 0)
|
|
2743
|
+
+ stream_data.get("frames_io_fail", 0)) == total
|
|
2744
|
+
avg = stream_data.get("interval_actual_avg_ms")
|
|
2745
|
+
report("stream.frames_conserved", "PASS" if conserved else "FAIL",
|
|
2746
|
+
f"total={total} (ok={stream_data.get('frames_ok')}, "
|
|
2747
|
+
f"ff_gap={stream_data.get('frames_ff_gap')}, io_fail={stream_data.get('frames_io_fail')})")
|
|
2748
|
+
report("stream.interval_perf", "PASS" if (avg is not None and avg <= 110) else "FAIL",
|
|
2749
|
+
f"avg={avg}ms (target 100ms, 验收线 <=110ms)")
|
|
2750
|
+
report("stream.io_overhead", "PASS" if stream_data.get("io_overhead_avg_ms") is not None else "FAIL",
|
|
2751
|
+
f"io_overhead_avg_ms={stream_data.get('io_overhead_avg_ms')}")
|
|
2752
|
+
report("stream.gaps", "PASS", f"gaps_n={stream_data.get('gaps_n', 0)} (仅 >2x target 计数)")
|
|
2753
|
+
else:
|
|
2754
|
+
report("stream", "FAIL", (body or {}).get("error_class", f"exit {rc}"))
|
|
2706
2755
|
else:
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2756
|
+
print("[2/4] stream 冒烟 (未配置 target_profile.stream_regs, 跳过) ...")
|
|
2757
|
+
report("stream", "SKIP", "未配置产品协议 profile (target_profile.stream_regs),寄存器级自验跳过")
|
|
2758
|
+
|
|
2759
|
+
# 3. 写读回环 (write_allowed 双门内; 未授权或未配 profile 时 SKIP, 不算 FAIL)
|
|
2760
|
+
if loop_cfg:
|
|
2761
|
+
loop_addr = str(loop_cfg.get("addr", "0xBE"))
|
|
2762
|
+
w_data = str(loop_cfg.get("write_data", "01"))
|
|
2763
|
+
r_data = str(loop_cfg.get("restore_data", "00"))
|
|
2764
|
+
print(f"[3/4] 写读回环 ({loop_addr}={w_data}->{r_data}, 需 lab.yaml write_allowed) ...")
|
|
2765
|
+
rc, body = run_cli("write-reg", "--addr", loop_addr, "--data", w_data, "--confirm")
|
|
2766
|
+
if rc == 0 and body and body.get("ok"):
|
|
2767
|
+
auth = (body.get("data") or {}).get("authorization") or {}
|
|
2768
|
+
report("write.readback", "PASS", f"{loop_addr}={w_data} 写入成功 (授权来源: {auth.get('granted_by', '?')})")
|
|
2769
|
+
run_cli("write-reg", "--addr", loop_addr, "--data", r_data, "--confirm")
|
|
2770
|
+
report("write.restore", "PASS", f"{loop_addr}={r_data} 已恢复")
|
|
2710
2771
|
else:
|
|
2711
|
-
|
|
2772
|
+
err_class = (body or {}).get("error_class", f"exit {rc}")
|
|
2773
|
+
if err_class == "PERMISSION_DENIED":
|
|
2774
|
+
report("write.readback", "SKIP", "write_allowed 未开启 (双门保护), 跳过写回环")
|
|
2775
|
+
else:
|
|
2776
|
+
report("write.readback", "FAIL", err_class)
|
|
2777
|
+
else:
|
|
2778
|
+
print("[3/4] 写读回环 (未配置 target_profile.write_loop, 跳过) ...")
|
|
2779
|
+
report("write.readback", "SKIP", "未配置产品协议 profile (target_profile.write_loop),写回环自验跳过")
|
|
2712
2780
|
|
|
2713
2781
|
# 3.5 write_allowed 卫生检查 (P1-2): 任何层级为 true 即 WARNING (会话级临时态)
|
|
2714
2782
|
print("[3.5/4] write_allowed 卫生检查 ...")
|
|
@@ -3139,7 +3207,7 @@ def set_ndtdbg_dlldir(dll_path: str) -> str:
|
|
|
3139
3207
|
def handle_cli_setup() -> int:
|
|
3140
3208
|
"""Handle `firmwareloop setup` command: simultaneously configure Antigravity CLI, Claude Code CLI, Qoder IDE, CodeBuddy IDE, DeepSeek Harness (DSH), and ZCode IDE."""
|
|
3141
3209
|
print("============================================================")
|
|
3142
|
-
print(" FirmwareLoop Multi-Agent Global Setup Helper (v0.
|
|
3210
|
+
print(" FirmwareLoop Multi-Agent Global Setup Helper (v0.9.1)")
|
|
3143
3211
|
print("============================================================")
|
|
3144
3212
|
print("")
|
|
3145
3213
|
|
|
@@ -3254,27 +3322,28 @@ def handle_cli_setup() -> int:
|
|
|
3254
3322
|
print(f" - User MCP: [Warning] Could not write {zc_result['path']}: {zc_result.get('reason')}")
|
|
3255
3323
|
|
|
3256
3324
|
# 7. Global Skills Sync for Antigravity and Claude Code
|
|
3325
|
+
# 源解析 (v0.9.1): 源码仓库 skills/<name>/SKILL.md 优先, 安装态回退包内
|
|
3326
|
+
# tools/templates/skill-<name>.md (skills/ 不随 wheel 分发, 此前安装态静默 0 同步)。
|
|
3327
|
+
print(f"\n[7] Global Skills:")
|
|
3257
3328
|
ag_skills_dir = os.path.join(user_home, ".gemini", "antigravity-cli", "skills")
|
|
3258
3329
|
claude_skills_dir = os.path.join(user_home, ".claude", "skills")
|
|
3259
|
-
source_skills_dir = os.path.join(REPO_ROOT, "skills")
|
|
3260
3330
|
skills_synced = 0
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
print(f"\n[7] Global Skills:")
|
|
3331
|
+
for sk in ["firmwareloop", "fwloop-adapter"]:
|
|
3332
|
+
sk_src = _skill_template_path(sk)
|
|
3333
|
+
if not sk_src:
|
|
3334
|
+
print(f" - {sk}: [Warning] skill 源缺失 (仓库 skills/ 与包内 templates/ 均未找到)")
|
|
3335
|
+
continue
|
|
3336
|
+
try:
|
|
3337
|
+
ag_target = os.path.join(ag_skills_dir, sk)
|
|
3338
|
+
os.makedirs(ag_target, exist_ok=True)
|
|
3339
|
+
shutil.copyfile(sk_src, os.path.join(ag_target, "SKILL.md"))
|
|
3340
|
+
|
|
3341
|
+
cl_target = os.path.join(claude_skills_dir, sk)
|
|
3342
|
+
os.makedirs(cl_target, exist_ok=True)
|
|
3343
|
+
shutil.copyfile(sk_src, os.path.join(cl_target, "SKILL.md"))
|
|
3344
|
+
skills_synced += 1
|
|
3345
|
+
except Exception as exc: # noqa: BLE001
|
|
3346
|
+
print(f" - {sk}: [Warning] {exc}")
|
|
3278
3347
|
print(f" - Synchronized: [OK] {skills_synced} skills (firmwareloop, fwloop-adapter) to Antigravity & Claude Code")
|
|
3279
3348
|
|
|
3280
3349
|
# 8. Machine-level global lab.yaml (ndtdbg config for the globally installed tool)
|
|
@@ -3294,7 +3363,7 @@ def handle_cli_setup() -> int:
|
|
|
3294
3363
|
def handle_cli_init(chip: Optional[str] = None, upgrade: bool = False) -> int:
|
|
3295
3364
|
"""Handle `firmwareloop init [target_chip]` command."""
|
|
3296
3365
|
print("============================================================")
|
|
3297
|
-
print(" FirmwareLoop Multi-Agent Project Initializer (v0.
|
|
3366
|
+
print(" FirmwareLoop Multi-Agent Project Initializer (v0.9.1)")
|
|
3298
3367
|
print("============================================================")
|
|
3299
3368
|
cwd = os.getcwd()
|
|
3300
3369
|
print(f"[*] Initializing multi-agent guidelines & bench config in:\n {cwd}\n")
|
|
@@ -3327,7 +3396,7 @@ def handle_cli_init(chip: Optional[str] = None, upgrade: bool = False) -> int:
|
|
|
3327
3396
|
|
|
3328
3397
|
|
|
3329
3398
|
def print_cli_help() -> None:
|
|
3330
|
-
print("""FirmwareLoop (fwloop) — AI Agent Firmware Engineering & Lab Automation Platform (v0.
|
|
3399
|
+
print("""FirmwareLoop (fwloop) — AI Agent Firmware Engineering & Lab Automation Platform (v0.9.1)
|
|
3331
3400
|
|
|
3332
3401
|
Usage:
|
|
3333
3402
|
fwloop [command] (or: firmwareloop [command])
|
|
@@ -3373,7 +3442,7 @@ def main() -> None:
|
|
|
3373
3442
|
sys.exit(2)
|
|
3374
3443
|
sys.exit(handle_cli_ndtdebugtool(args[1]))
|
|
3375
3444
|
elif cmd in ["version", "-v", "--version"]:
|
|
3376
|
-
print("FirmwareLoop v0.
|
|
3445
|
+
print("FirmwareLoop v0.9.1")
|
|
3377
3446
|
sys.exit(0)
|
|
3378
3447
|
elif cmd in ["help", "-h", "--help"]:
|
|
3379
3448
|
print_cli_help()
|