firmwareloop 0.2.2__tar.gz → 0.4.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.
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/PKG-INFO +3 -3
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/README.md +2 -2
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/firmwareloop.egg-info/PKG-INFO +3 -3
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/pyproject.toml +1 -1
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/__init__.py +1 -1
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/fw_mcp_server.py +112 -8
- firmwareloop-0.4.0/tools/lib/ndtdbg.py +794 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/ndtdbg_bridge.ps1 +171 -33
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/ndtdbg_cli.py +28 -4
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/setup-agent-mcp.ps1 +2 -2
- firmwareloop-0.2.2/tools/lib/ndtdbg.py +0 -512
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/LICENSE +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/firmwareloop.egg-info/SOURCES.txt +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/firmwareloop.egg-info/dependency_links.txt +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/firmwareloop.egg-info/entry_points.txt +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/firmwareloop.egg-info/requires.txt +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/firmwareloop.egg-info/top_level.txt +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/setup.cfg +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/acceptance-scenario.ps1 +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/build.ps1 +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/can.ps1 +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/check-qoder-mcp.ps1 +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/common/build-backends.psm1 +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/common/fw.psm1 +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/common/uart_probe.py +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/doctor.ps1 +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/flash.ps1 +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/instrument_cli.py +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/lib/__init__.py +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/lib/instruments.py +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/logic_capture.ps1 +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/logic_decode.ps1 +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/reset.ps1 +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/sigrok_csv_to_fwloop.py +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/tools/test-backends.ps1 +0 -0
- {firmwareloop-0.2.2 → firmwareloop-0.4.0}/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.4.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
|
|
@@ -381,7 +381,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
381
381
|
├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
|
|
382
382
|
├── demo-make/ 示例固件(Make;构建测试载体)
|
|
383
383
|
├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
|
|
384
|
-
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.
|
|
384
|
+
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.4.0)
|
|
385
385
|
├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
|
|
386
386
|
├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
|
|
387
387
|
├── CLAUDE.md Claude Code CLI 指南
|
|
@@ -391,7 +391,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
391
391
|
|
|
392
392
|
---
|
|
393
393
|
|
|
394
|
-
## 能力验证状态(v0.
|
|
394
|
+
## 能力验证状态(v0.4.0)
|
|
395
395
|
|
|
396
396
|
> 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
|
|
397
397
|
> `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
|
|
@@ -353,7 +353,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
353
353
|
├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
|
|
354
354
|
├── demo-make/ 示例固件(Make;构建测试载体)
|
|
355
355
|
├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
|
|
356
|
-
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.
|
|
356
|
+
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.4.0)
|
|
357
357
|
├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
|
|
358
358
|
├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
|
|
359
359
|
├── CLAUDE.md Claude Code CLI 指南
|
|
@@ -363,7 +363,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
363
363
|
|
|
364
364
|
---
|
|
365
365
|
|
|
366
|
-
## 能力验证状态(v0.
|
|
366
|
+
## 能力验证状态(v0.4.0)
|
|
367
367
|
|
|
368
368
|
> 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
|
|
369
369
|
> `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: firmwareloop
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.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
|
|
@@ -381,7 +381,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
381
381
|
├── demo-firmware/ 示例固件(CMake;宿主编译模拟 MCU,闭环验证载体)
|
|
382
382
|
├── demo-make/ 示例固件(Make;构建测试载体)
|
|
383
383
|
├── docs/ DEPENDENCY_MATRIX / REUSE_PLAN / V0.0.2_GAP_VERIFICATION
|
|
384
|
-
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.
|
|
384
|
+
├── pyproject.toml 标准 Python 包配置与 CLI 入口声明 (v0.4.0)
|
|
385
385
|
├── .mcp.example.json 双层 MCP 配置模板(firmwareloop + agentic-hil)
|
|
386
386
|
├── AGENTS.md 通用智能体规范(Antigravity / Qoder / Cursor 等)
|
|
387
387
|
├── CLAUDE.md Claude Code CLI 指南
|
|
@@ -391,7 +391,7 @@ claude mcp remove --scope user agentic-hil
|
|
|
391
391
|
|
|
392
392
|
---
|
|
393
393
|
|
|
394
|
-
## 能力验证状态(v0.
|
|
394
|
+
## 能力验证状态(v0.4.0)
|
|
395
395
|
|
|
396
396
|
> 状态定义:`Implemented`(已实现)/ `Simulator Validated`(模拟验证)/
|
|
397
397
|
> `Real Hardware Validated`(真机验证)/ `Experimental` / `Not Implemented`
|
|
@@ -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.4.0).
|
|
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
|
|
@@ -360,6 +360,8 @@ def handle_fw_ndtdbg_stream(args: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
360
360
|
cmd.extend(["--fail-threshold", str(int(args["fail_threshold"]))])
|
|
361
361
|
if args.get("require_target"):
|
|
362
362
|
cmd.append("--require-target")
|
|
363
|
+
if args.get("pre_write"):
|
|
364
|
+
cmd.extend(["--pre-write", *[str(p) for p in args["pre_write"]]])
|
|
363
365
|
_ndtdbg_reg_args(args, cmd)
|
|
364
366
|
return _ndtdbg_run(cmd, timeout=int(duration_s) + 120)
|
|
365
367
|
|
|
@@ -1387,7 +1389,7 @@ TOOLS_REGISTRY = {
|
|
|
1387
1389
|
"handler": handle_fw_ndtdbg_flash,
|
|
1388
1390
|
},
|
|
1389
1391
|
"fw_ndtdbg_stream": {
|
|
1390
|
-
"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. Read-only. Writes CSV with timestamps; returns JSON summary (frames, FF_GAP/IO_FAIL counts, interval stats).
|
|
1392
|
+
"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. Read-only. Writes CSV with timestamps; returns JSON summary (frames, FF_GAP/IO_FAIL counts, interval stats, io_overhead). Supports two protocol profiles: continuous (pure-read polling) and request-response (pre_write command written before every frame, e.g. PT488 0x10/0x14 channels). Per-frame IO overhead is reported separately in io_overhead_avg_ms.",
|
|
1391
1393
|
"inputSchema": {
|
|
1392
1394
|
"type": "object",
|
|
1393
1395
|
"properties": {
|
|
@@ -1437,6 +1439,11 @@ TOOLS_REGISTRY = {
|
|
|
1437
1439
|
"description": "Probe reg 0x10 before capture; abort with TARGET_MISMATCH when the chip does not answer.",
|
|
1438
1440
|
"default": False,
|
|
1439
1441
|
},
|
|
1442
|
+
"pre_write": {
|
|
1443
|
+
"type": "array",
|
|
1444
|
+
"items": {"type": "string"},
|
|
1445
|
+
"description": "Request-response protocol profile: commands 'addr:data' written before EVERY frame (e.g. ['0xEC:10']) so request-response channels refresh each read. Omit for continuous (pure-read) streams. Defaults from lab.yaml ndtdbg.stream.pre_write when omitted.",
|
|
1446
|
+
},
|
|
1440
1447
|
"dll_dir": {"type": "string", "description": "NdtDebugTool lib dir; default from lab.yaml ndtdbg.dll_dir"},
|
|
1441
1448
|
"slave_addr": {"type": "string", "description": "IIC slave address, e.g. 0xA0; default from lab.yaml"},
|
|
1442
1449
|
},
|
|
@@ -1619,7 +1626,7 @@ def process_request(req: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
|
1619
1626
|
},
|
|
1620
1627
|
"serverInfo": {
|
|
1621
1628
|
"name": "firmwareloop",
|
|
1622
|
-
"version": "0.
|
|
1629
|
+
"version": "0.4.0"
|
|
1623
1630
|
}
|
|
1624
1631
|
}
|
|
1625
1632
|
}
|
|
@@ -1754,7 +1761,7 @@ def _settle_stashed_entrypoints(stashed) -> None:
|
|
|
1754
1761
|
def handle_cli_update() -> int:
|
|
1755
1762
|
"""Handle `firmwareloop update` command."""
|
|
1756
1763
|
print("============================================================")
|
|
1757
|
-
print(" FirmwareLoop Auto-Updater (v0.
|
|
1764
|
+
print(" FirmwareLoop Auto-Updater (v0.4.0)")
|
|
1758
1765
|
print("============================================================")
|
|
1759
1766
|
|
|
1760
1767
|
is_git_repo = os.path.exists(os.path.join(REPO_ROOT, ".git"))
|
|
@@ -1823,6 +1830,100 @@ def handle_cli_update() -> int:
|
|
|
1823
1830
|
return res.returncode
|
|
1824
1831
|
|
|
1825
1832
|
|
|
1833
|
+
def handle_cli_selftest(target: str = "ndtdbg") -> int:
|
|
1834
|
+
"""`fwloop selftest ndtdbg`: 按 BACKLOG 验收标准自动回归 (需硬件在环)。
|
|
1835
|
+
|
|
1836
|
+
流程: status 分层探测 -> stream 冒烟 (100ms x 10s, 帧守恒/性能线/gaps 断言) ->
|
|
1837
|
+
写读回环 (write_allowed 双门内, 未授权 SKIP) -> 结构化汇总。任一 FAIL 则 exit 1。
|
|
1838
|
+
"""
|
|
1839
|
+
if target != "ndtdbg":
|
|
1840
|
+
print(f"[-] Unknown selftest target: {target} (supported: ndtdbg)")
|
|
1841
|
+
return 2
|
|
1842
|
+
print("============================================================")
|
|
1843
|
+
print(" FirmwareLoop Self-Test: ndtdbg (BACKLOG 验收标准)")
|
|
1844
|
+
print("============================================================")
|
|
1845
|
+
results: list = []
|
|
1846
|
+
|
|
1847
|
+
def run_cli(*args, timeout=120):
|
|
1848
|
+
cli = os.path.join(REPO_ROOT, "tools", "ndtdbg_cli.py")
|
|
1849
|
+
try:
|
|
1850
|
+
proc = subprocess.run([sys.executable, cli, *args], capture_output=True,
|
|
1851
|
+
text=True, encoding="utf-8", errors="replace", timeout=timeout)
|
|
1852
|
+
except subprocess.TimeoutExpired:
|
|
1853
|
+
return -1, {"ok": False, "error_class": "INSTRUMENT_TIMEOUT",
|
|
1854
|
+
"error": f"子命令超时 >{timeout}s (疑似 USB 会话被残留进程占用或设备挂死)"}
|
|
1855
|
+
body = None
|
|
1856
|
+
for line in reversed((proc.stdout or "").strip().splitlines()):
|
|
1857
|
+
line = line.strip()
|
|
1858
|
+
if line.startswith("{"):
|
|
1859
|
+
try:
|
|
1860
|
+
body = json.loads(line)
|
|
1861
|
+
break
|
|
1862
|
+
except Exception:
|
|
1863
|
+
continue
|
|
1864
|
+
return proc.returncode, body
|
|
1865
|
+
|
|
1866
|
+
def report(name, status, detail=""):
|
|
1867
|
+
results.append((name, status, detail))
|
|
1868
|
+
|
|
1869
|
+
# 1. status 分层探测
|
|
1870
|
+
print("\n[1/4] status 探测 (Box / IIC bus / target chip) ...")
|
|
1871
|
+
rc, body = run_cli("status", timeout=30)
|
|
1872
|
+
if rc == 0 and body and body.get("ok"):
|
|
1873
|
+
data = body.get("data") or {}
|
|
1874
|
+
report("status", "PASS", data.get("summary", ""))
|
|
1875
|
+
else:
|
|
1876
|
+
report("status", "FAIL", (body or {}).get("error_class", f"exit {rc}"))
|
|
1877
|
+
|
|
1878
|
+
# 2. stream 冒烟 (100ms x 10s)
|
|
1879
|
+
print("[2/4] stream 冒烟 (100ms x 10s, int16 解码) ...")
|
|
1880
|
+
rc, body = run_cli("stream", "--regs", "0xEE:8:ch", "--interval-ms", "100",
|
|
1881
|
+
"--duration-s", "10", "--decode", "int16le", timeout=60)
|
|
1882
|
+
stream_data = (body or {}).get("data") if isinstance(body, dict) else None
|
|
1883
|
+
if rc == 0 and stream_data:
|
|
1884
|
+
total = stream_data.get("frames_total", 0)
|
|
1885
|
+
conserved = (stream_data.get("frames_ok", 0) + stream_data.get("frames_ff_gap", 0)
|
|
1886
|
+
+ stream_data.get("frames_io_fail", 0)) == total
|
|
1887
|
+
avg = stream_data.get("interval_actual_avg_ms")
|
|
1888
|
+
report("stream.frames_conserved", "PASS" if conserved else "FAIL",
|
|
1889
|
+
f"total={total} (ok={stream_data.get('frames_ok')}, "
|
|
1890
|
+
f"ff_gap={stream_data.get('frames_ff_gap')}, io_fail={stream_data.get('frames_io_fail')})")
|
|
1891
|
+
report("stream.interval_perf", "PASS" if (avg is not None and avg <= 110) else "FAIL",
|
|
1892
|
+
f"avg={avg}ms (target 100ms, 验收线 <=110ms)")
|
|
1893
|
+
report("stream.io_overhead", "PASS" if stream_data.get("io_overhead_avg_ms") is not None else "FAIL",
|
|
1894
|
+
f"io_overhead_avg_ms={stream_data.get('io_overhead_avg_ms')}")
|
|
1895
|
+
report("stream.gaps", "PASS", f"gaps_n={stream_data.get('gaps_n', 0)} (仅 >2x target 计数)")
|
|
1896
|
+
else:
|
|
1897
|
+
report("stream", "FAIL", (body or {}).get("error_class", f"exit {rc}"))
|
|
1898
|
+
|
|
1899
|
+
# 3. 写读回环 (write_allowed 双门内; 未授权 SKIP, 不算 FAIL)
|
|
1900
|
+
print("[3/4] 写读回环 (0xBE debug_mode, 需 lab.yaml write_allowed) ...")
|
|
1901
|
+
rc, body = run_cli("write-reg", "--addr", "0xBE", "--data", "01", "--confirm")
|
|
1902
|
+
if rc == 0 and body and body.get("ok"):
|
|
1903
|
+
report("write.readback", "PASS", "0xBE=01 写入成功 (debug_mode 开启)")
|
|
1904
|
+
run_cli("write-reg", "--addr", "0xBE", "--data", "00", "--confirm")
|
|
1905
|
+
report("write.restore", "PASS", "0xBE=00 已恢复")
|
|
1906
|
+
else:
|
|
1907
|
+
err_class = (body or {}).get("error_class", f"exit {rc}")
|
|
1908
|
+
if err_class == "PERMISSION_DENIED":
|
|
1909
|
+
report("write.readback", "SKIP", "write_allowed 未开启 (双门保护), 跳过写回环")
|
|
1910
|
+
else:
|
|
1911
|
+
report("write.readback", "FAIL", err_class)
|
|
1912
|
+
|
|
1913
|
+
# 4. 汇总
|
|
1914
|
+
print("\n============================================================")
|
|
1915
|
+
for name, status, detail in results:
|
|
1916
|
+
print(f" [{status}] {name}" + (f" — {detail}" if detail else ""))
|
|
1917
|
+
failed = [r for r in results if r[1] == "FAIL"]
|
|
1918
|
+
skipped = [r for r in results if r[1] == "SKIP"]
|
|
1919
|
+
overall = "FAIL" if failed else "PASS"
|
|
1920
|
+
print("============================================================")
|
|
1921
|
+
print(f" Self-Test Result: {overall} "
|
|
1922
|
+
f"({len(results) - len(failed) - len(skipped)} pass, {len(failed)} fail, {len(skipped)} skip)")
|
|
1923
|
+
print("============================================================")
|
|
1924
|
+
return 1 if failed else 0
|
|
1925
|
+
|
|
1926
|
+
|
|
1826
1927
|
def handle_cli_doctor() -> int:
|
|
1827
1928
|
"""Handle `firmwareloop doctor` command."""
|
|
1828
1929
|
print("[*] Running FirmwareLoop environment diagnostics...")
|
|
@@ -2029,7 +2130,7 @@ def set_ndtdbg_dlldir(dll_path: str) -> str:
|
|
|
2029
2130
|
def handle_cli_setup() -> int:
|
|
2030
2131
|
"""Handle `firmwareloop setup` command: simultaneously configure Antigravity CLI, Claude Code CLI, Qoder IDE, and CodeBuddy IDE."""
|
|
2031
2132
|
print("============================================================")
|
|
2032
|
-
print(" FirmwareLoop Multi-Agent Global Setup Helper (v0.
|
|
2133
|
+
print(" FirmwareLoop Multi-Agent Global Setup Helper (v0.4.0)")
|
|
2033
2134
|
print("============================================================")
|
|
2034
2135
|
print("")
|
|
2035
2136
|
|
|
@@ -2154,7 +2255,7 @@ def handle_cli_setup() -> int:
|
|
|
2154
2255
|
def handle_cli_init() -> int:
|
|
2155
2256
|
"""Handle `firmwareloop init` command."""
|
|
2156
2257
|
print("============================================================")
|
|
2157
|
-
print(" FirmwareLoop Multi-Agent Project Initializer (v0.
|
|
2258
|
+
print(" FirmwareLoop Multi-Agent Project Initializer (v0.4.0)")
|
|
2158
2259
|
print("============================================================")
|
|
2159
2260
|
cwd = os.getcwd()
|
|
2160
2261
|
print(f"[*] Initializing multi-agent guidelines & bench config in:\n {cwd}\n")
|
|
@@ -2175,7 +2276,7 @@ def handle_cli_init() -> int:
|
|
|
2175
2276
|
|
|
2176
2277
|
|
|
2177
2278
|
def print_cli_help() -> None:
|
|
2178
|
-
print("""FirmwareLoop (fwloop) — AI Agent Firmware Engineering & Lab Automation Platform (v0.
|
|
2279
|
+
print("""FirmwareLoop (fwloop) — AI Agent Firmware Engineering & Lab Automation Platform (v0.4.0)
|
|
2179
2280
|
|
|
2180
2281
|
Usage:
|
|
2181
2282
|
fwloop [command] (or: firmwareloop [command])
|
|
@@ -2184,6 +2285,7 @@ Commands:
|
|
|
2184
2285
|
init Initialize multi-agent rules (AGENTS.md, CLAUDE.md, GEMINI.md) & lab.yaml in current project
|
|
2185
2286
|
update Check and update FirmwareLoop to the latest version
|
|
2186
2287
|
doctor Run environment & toolchain diagnostics
|
|
2288
|
+
selftest [target] Hardware self-test regression (default: ndtdbg; BACKLOG acceptance)
|
|
2187
2289
|
setup Register AI Agent MCP globally & generate machine-level config (%APPDATA%\\FirmwareLoop\\lab.yaml)
|
|
2188
2290
|
ndtdebugtool DIR Configure NdtBox: point to the NdtDebugTool folder root (auto-resolves its lib/)
|
|
2189
2291
|
dlldir DIR Alias of ndtdebugtool (legacy name, pass the lib/ directory directly)
|
|
@@ -2205,6 +2307,8 @@ def main() -> None:
|
|
|
2205
2307
|
sys.exit(handle_cli_update())
|
|
2206
2308
|
elif cmd in ["doctor", "check"]:
|
|
2207
2309
|
sys.exit(handle_cli_doctor())
|
|
2310
|
+
elif cmd == "selftest":
|
|
2311
|
+
sys.exit(handle_cli_selftest(args[1] if len(args) > 1 else "ndtdbg"))
|
|
2208
2312
|
elif cmd in ["setup", "register"]:
|
|
2209
2313
|
sys.exit(handle_cli_setup())
|
|
2210
2314
|
elif cmd in ["ndtdebugtool", "dlldir"]:
|
|
@@ -2215,7 +2319,7 @@ def main() -> None:
|
|
|
2215
2319
|
sys.exit(2)
|
|
2216
2320
|
sys.exit(handle_cli_ndtdebugtool(args[1]))
|
|
2217
2321
|
elif cmd in ["version", "-v", "--version"]:
|
|
2218
|
-
print("FirmwareLoop v0.
|
|
2322
|
+
print("FirmwareLoop v0.4.0")
|
|
2219
2323
|
sys.exit(0)
|
|
2220
2324
|
elif cmd in ["help", "-h", "--help"]:
|
|
2221
2325
|
print_cli_help()
|