nff 0.2.0__tar.gz → 0.2.2__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.
- {nff-0.2.0 → nff-0.2.2}/PKG-INFO +1 -1
- {nff-0.2.0 → nff-0.2.2}/nff/cli.py +2 -0
- nff-0.2.2/nff/commands/clean.py +117 -0
- {nff-0.2.0 → nff-0.2.2}/nff/commands/init.py +66 -35
- {nff-0.2.0 → nff-0.2.2}/nff.egg-info/PKG-INFO +1 -1
- {nff-0.2.0 → nff-0.2.2}/nff.egg-info/SOURCES.txt +1 -0
- {nff-0.2.0 → nff-0.2.2}/pyproject.toml +1 -1
- {nff-0.2.0 → nff-0.2.2}/LICENSE +0 -0
- {nff-0.2.0 → nff-0.2.2}/README.md +0 -0
- {nff-0.2.0 → nff-0.2.2}/nff/__init__.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/nff/commands/__init__.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/nff/commands/doctor.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/nff/commands/flash.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/nff/commands/monitor.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/nff/commands/wokwi.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/nff/config.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/nff/mcp_server.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/nff/tools/__init__.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/nff/tools/boards.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/nff/tools/installer.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/nff/tools/serial.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/nff/tools/toolchain.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/nff/tools/wokwi.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/nff/tools/wokwi_installer.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/nff.egg-info/dependency_links.txt +0 -0
- {nff-0.2.0 → nff-0.2.2}/nff.egg-info/entry_points.txt +0 -0
- {nff-0.2.0 → nff-0.2.2}/nff.egg-info/requires.txt +0 -0
- {nff-0.2.0 → nff-0.2.2}/nff.egg-info/top_level.txt +0 -0
- {nff-0.2.0 → nff-0.2.2}/setup.cfg +0 -0
- {nff-0.2.0 → nff-0.2.2}/tests/test_boards.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/tests/test_config.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/tests/test_doctor.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/tests/test_mcp.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/tests/test_serial.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/tests/test_toolchain.py +0 -0
- {nff-0.2.0 → nff-0.2.2}/tests/test_wokwi.py +0 -0
{nff-0.2.0 → nff-0.2.2}/PKG-INFO
RENAMED
|
@@ -38,12 +38,14 @@ from nff.commands.flash import flash # noqa: E402
|
|
|
38
38
|
from nff.commands.monitor import monitor # noqa: E402
|
|
39
39
|
from nff.commands.doctor import doctor # noqa: E402
|
|
40
40
|
from nff.commands.wokwi import wokwi # noqa: E402
|
|
41
|
+
from nff.commands.clean import clean # noqa: E402
|
|
41
42
|
|
|
42
43
|
cli.add_command(init)
|
|
43
44
|
cli.add_command(flash)
|
|
44
45
|
cli.add_command(monitor)
|
|
45
46
|
cli.add_command(doctor)
|
|
46
47
|
cli.add_command(wokwi)
|
|
48
|
+
cli.add_command(clean)
|
|
47
49
|
|
|
48
50
|
|
|
49
51
|
@cli.command("install-deps")
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""nff clean — remove the MCP server registration and optionally the nff config."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
import click
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
|
|
14
|
+
from nff import config as cfg_module
|
|
15
|
+
|
|
16
|
+
if sys.platform == "win32" and hasattr(sys.stdout, "reconfigure"):
|
|
17
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
18
|
+
|
|
19
|
+
console = Console(legacy_windows=False)
|
|
20
|
+
|
|
21
|
+
_CLAUDE_DESKTOP_CONFIG = Path.home() / ".claude" / "claude_desktop_config.json"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _remove_from_claude_code() -> bool:
|
|
25
|
+
"""Run `claude mcp remove nff`. Returns True on success."""
|
|
26
|
+
if not shutil.which("claude"):
|
|
27
|
+
return False
|
|
28
|
+
try:
|
|
29
|
+
result = subprocess.run(
|
|
30
|
+
["claude", "mcp", "remove", "nff"],
|
|
31
|
+
capture_output=True,
|
|
32
|
+
text=True,
|
|
33
|
+
timeout=15,
|
|
34
|
+
)
|
|
35
|
+
return result.returncode == 0
|
|
36
|
+
except (OSError, subprocess.TimeoutExpired):
|
|
37
|
+
return False
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _remove_from_claude_desktop_config() -> bool:
|
|
41
|
+
"""Remove the nff mcpServers entry from claude_desktop_config.json.
|
|
42
|
+
|
|
43
|
+
Returns True if the entry was present and removed, False if it wasn't there.
|
|
44
|
+
"""
|
|
45
|
+
if not _CLAUDE_DESKTOP_CONFIG.exists():
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
raw = _CLAUDE_DESKTOP_CONFIG.read_text(encoding="utf-8")
|
|
49
|
+
if not raw.strip():
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
data: dict = json.loads(raw)
|
|
53
|
+
servers: dict = data.get("mcpServers", {})
|
|
54
|
+
if "nff" not in servers:
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
del servers["nff"]
|
|
58
|
+
if not servers:
|
|
59
|
+
data.pop("mcpServers", None)
|
|
60
|
+
|
|
61
|
+
_CLAUDE_DESKTOP_CONFIG.write_text(json.dumps(data, indent=2), encoding="utf-8")
|
|
62
|
+
return True
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@click.command()
|
|
66
|
+
@click.option(
|
|
67
|
+
"--config",
|
|
68
|
+
"remove_config",
|
|
69
|
+
is_flag=True,
|
|
70
|
+
help="Also delete ~/.nff/config.json.",
|
|
71
|
+
)
|
|
72
|
+
def clean(remove_config: bool) -> None:
|
|
73
|
+
"""Remove the nff MCP server registration from Claude Code and Claude Desktop."""
|
|
74
|
+
any_removed = False
|
|
75
|
+
|
|
76
|
+
# Claude Code CLI
|
|
77
|
+
if _remove_from_claude_code():
|
|
78
|
+
console.print(
|
|
79
|
+
" [bold green]✓[/bold green] Removed from Claude Code "
|
|
80
|
+
"([bold]claude mcp remove nff[/bold])"
|
|
81
|
+
)
|
|
82
|
+
any_removed = True
|
|
83
|
+
else:
|
|
84
|
+
console.print(
|
|
85
|
+
" [dim]`claude` CLI not found or nff was not registered — skipping.[/dim]"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Claude Desktop JSON config
|
|
89
|
+
try:
|
|
90
|
+
removed = _remove_from_claude_desktop_config()
|
|
91
|
+
if removed:
|
|
92
|
+
console.print(
|
|
93
|
+
f" [bold green]✓[/bold green] Removed nff entry from "
|
|
94
|
+
f"[bold]{_CLAUDE_DESKTOP_CONFIG}[/bold]"
|
|
95
|
+
)
|
|
96
|
+
any_removed = True
|
|
97
|
+
else:
|
|
98
|
+
console.print(
|
|
99
|
+
f" [dim]nff not found in {_CLAUDE_DESKTOP_CONFIG} — nothing to remove.[/dim]"
|
|
100
|
+
)
|
|
101
|
+
except (ValueError, OSError) as exc:
|
|
102
|
+
console.print(f" [yellow]⚠[/yellow] Could not update Claude Desktop config: {exc}")
|
|
103
|
+
|
|
104
|
+
# Optional: nff config file
|
|
105
|
+
if remove_config:
|
|
106
|
+
config_path = cfg_module.CONFIG_PATH
|
|
107
|
+
if config_path.exists():
|
|
108
|
+
config_path.unlink()
|
|
109
|
+
console.print(
|
|
110
|
+
f" [bold green]✓[/bold green] Deleted [bold]{config_path}[/bold]"
|
|
111
|
+
)
|
|
112
|
+
any_removed = True
|
|
113
|
+
else:
|
|
114
|
+
console.print(f" [dim]{config_path} does not exist — nothing to delete.[/dim]")
|
|
115
|
+
|
|
116
|
+
if not any_removed:
|
|
117
|
+
console.print(" Nothing to clean.")
|
|
@@ -80,13 +80,24 @@ def _pick_device(devices: list[DetectedDevice]) -> DetectedDevice:
|
|
|
80
80
|
def _register_mcp_claude_code() -> bool:
|
|
81
81
|
"""Register nff with Claude Code CLI via `claude mcp add`.
|
|
82
82
|
|
|
83
|
+
Uses the absolute path of the running nff executable so Claude Code can
|
|
84
|
+
locate it on Windows where bare-name resolution may fail.
|
|
85
|
+
|
|
83
86
|
Returns True on success, False if `claude` is not in PATH or the command fails.
|
|
84
87
|
"""
|
|
85
88
|
if not shutil.which("claude"):
|
|
86
89
|
return False
|
|
90
|
+
|
|
91
|
+
nff_exe = shutil.which("nff") or sys.executable
|
|
92
|
+
# On Windows prefer the .exe alongside the current interpreter if found
|
|
93
|
+
if sys.platform == "win32":
|
|
94
|
+
candidate = Path(sys.executable).parent / "nff.exe"
|
|
95
|
+
if candidate.exists():
|
|
96
|
+
nff_exe = str(candidate)
|
|
97
|
+
|
|
87
98
|
try:
|
|
88
99
|
result = subprocess.run(
|
|
89
|
-
["claude", "mcp", "add", "nff",
|
|
100
|
+
["claude", "mcp", "add", "nff", nff_exe, "mcp"],
|
|
90
101
|
capture_output=True,
|
|
91
102
|
text=True,
|
|
92
103
|
timeout=15,
|
|
@@ -201,6 +212,7 @@ _CLAUDE_MD_TEMPLATE = """\
|
|
|
201
212
|
- Never install libraries with arduino-cli. Write sketches that use built-in APIs,
|
|
202
213
|
or ask the user to install the library first.
|
|
203
214
|
- For ESP32 servo/PWM use ledcAttach + ledcWrite (built-in LEDC, no library needed).
|
|
215
|
+
- Write sketches in the **current directory**: `<name>.ino` (not in a subdirectory).
|
|
204
216
|
|
|
205
217
|
## Connected Device
|
|
206
218
|
- Board : {board}
|
|
@@ -210,10 +222,38 @@ _CLAUDE_MD_TEMPLATE = """\
|
|
|
210
222
|
|
|
211
223
|
---
|
|
212
224
|
|
|
225
|
+
## nff CLI Commands
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
nff mcp start the MCP server (Claude Code calls this automatically)
|
|
229
|
+
nff flash <sketch.ino> compile + upload to {port}
|
|
230
|
+
nff flash <sketch.ino> --sim compile + simulate with Wokwi (no hardware needed)
|
|
231
|
+
nff flash <sketch.ino> --board <fqbn> override board FQBN
|
|
232
|
+
nff flash <sketch.ino> --port <port> override serial port
|
|
233
|
+
nff monitor open interactive serial monitor on {port}
|
|
234
|
+
nff monitor --port <port> --baud <n> override port / baud rate
|
|
235
|
+
nff wokwi init scaffold wokwi.toml + diagram.json in cwd
|
|
236
|
+
nff wokwi run run headless Wokwi simulation, stream serial output
|
|
237
|
+
nff wokwi run --gui open animated circuit in VS Code
|
|
238
|
+
nff wokwi run --timeout <ms> set simulation wall-clock timeout (default 5000 ms)
|
|
239
|
+
nff wokwi run --serial-log <file> write captured serial output to a file
|
|
240
|
+
nff doctor check all dependencies and config
|
|
241
|
+
nff init re-detect board, rewrite config + CLAUDE.md
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
213
246
|
## Workflow — Real Hardware
|
|
214
247
|
|
|
248
|
+
Iteration loop:
|
|
249
|
+
1. Write / edit `<name>.ino` in the current directory.
|
|
250
|
+
2. `nff flash <name>.ino` — compile and upload.
|
|
251
|
+
3. `nff monitor` — inspect serial output (Ctrl+C to quit).
|
|
252
|
+
4. Fix bugs, repeat from step 2.
|
|
253
|
+
|
|
254
|
+
MCP tools (called by Claude, no terminal needed):
|
|
215
255
|
```
|
|
216
|
-
flash(code) compile + upload sketch
|
|
256
|
+
flash(code) compile + upload sketch
|
|
217
257
|
serial_read(3000) capture 3 s of serial output
|
|
218
258
|
serial_write(data) send a string to the device
|
|
219
259
|
reset_device() hardware reset via DTR toggle
|
|
@@ -221,29 +261,24 @@ list_devices() verify board is connected
|
|
|
221
261
|
get_device_info() port / board / FQBN / baud
|
|
222
262
|
```
|
|
223
263
|
|
|
224
|
-
Iteration loop:
|
|
225
|
-
1. flash(code) — upload sketch
|
|
226
|
-
2. serial_read(3000) — read output
|
|
227
|
-
3. Fix bugs, repeat
|
|
228
|
-
|
|
229
264
|
---
|
|
230
265
|
|
|
231
266
|
## Workflow — Wokwi Simulation (no hardware needed)
|
|
232
267
|
|
|
233
|
-
### Quick
|
|
234
|
-
1. wokwi_flash(code, board="{fqbn}") — compile + simulate, returns
|
|
235
|
-
2. Inspect serial_output — no USB cable required
|
|
236
|
-
3. Iterate until output is correct
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
268
|
+
### Quick — headless via MCP tool
|
|
269
|
+
1. `wokwi_flash(code, board="{fqbn}")` — compile + simulate, returns serial output.
|
|
270
|
+
2. Inspect `serial_output` — no USB cable required.
|
|
271
|
+
3. Iterate until output is correct, then `flash(code)` to upload to real hardware.
|
|
272
|
+
|
|
273
|
+
### Full pipeline — visual circuit
|
|
274
|
+
1. Write `<name>.ino` in the current directory.
|
|
275
|
+
2. `nff wokwi init --board {fqbn}` — creates `wokwi.toml` + `diagram.json`.
|
|
276
|
+
3. Edit `diagram.json` to add components (LEDs, buttons, servos…).
|
|
277
|
+
4. `nff flash <name>.ino --sim --board {fqbn}` — compile.
|
|
278
|
+
5. Update `wokwi.toml` firmware path:
|
|
279
|
+
firmware = "build/{fqbn_dotted}/<name>.ino.elf"
|
|
280
|
+
6. `nff wokwi run --gui` — opens animated circuit in VS Code.
|
|
281
|
+
7. Fix bugs, repeat from step 4.
|
|
247
282
|
|
|
248
283
|
---
|
|
249
284
|
|
|
@@ -258,17 +293,13 @@ ESP32 pin names: esp:D<gpio> esp:GND.1 esp:GND.2 esp:3V3 esp:VIN
|
|
|
258
293
|
Common components:
|
|
259
294
|
wokwi-led attrs: color (red/green/blue/yellow)
|
|
260
295
|
wokwi-pushbutton attrs: color — pins: btn:1.l (gpio), btn:2.l (GND)
|
|
261
|
-
wokwi-servo attrs: minAngle, maxAngle — pins: PWM, V+, GND
|
|
296
|
+
wokwi-servo attrs: minAngle "-90", maxAngle "90" — pins: PWM, V+, GND
|
|
262
297
|
wokwi-resistor attrs: value (ohms)
|
|
263
298
|
wokwi-ntc-temperature-sensor
|
|
264
299
|
|
|
265
|
-
Pushbutton wiring:
|
|
300
|
+
Pushbutton wiring (INPUT_PULLUP):
|
|
266
301
|
["esp:D15", "btn1:1.l", "green", []]
|
|
267
302
|
["esp:GND.2", "btn1:2.l", "black", []]
|
|
268
|
-
pinMode(BUTTON_PIN, INPUT_PULLUP) in sketch
|
|
269
|
-
|
|
270
|
-
Servo attrs for correct visual mapping:
|
|
271
|
-
{{ "minAngle": "-90", "maxAngle": "90" }}
|
|
272
303
|
|
|
273
304
|
---
|
|
274
305
|
|
|
@@ -291,16 +322,16 @@ ledcWrite(SERVO_PIN, 4915); // center position
|
|
|
291
322
|
## Debugging
|
|
292
323
|
|
|
293
324
|
Simulation:
|
|
294
|
-
- Compile error
|
|
295
|
-
- Wrong output
|
|
296
|
-
- Component silent
|
|
297
|
-
- Servo wrong angle→ verify duty values match the 500–2500 µs Wokwi range
|
|
298
|
-
- Button skipped
|
|
325
|
+
- Compile error → fix sketch, re-run nff flash --sim
|
|
326
|
+
- Wrong output → nff wokwi run --serial-log out.txt, inspect out.txt
|
|
327
|
+
- Component silent → check diagram.json pin names and connection direction
|
|
328
|
+
- Servo wrong angle → verify duty values match the 500–2500 µs Wokwi range
|
|
329
|
+
- Button skipped → ensure INPUT_PULLUP and wiring gpio→btn:1.l, GND→btn:2.l
|
|
299
330
|
|
|
300
331
|
Hardware:
|
|
301
|
-
- Port not found
|
|
302
|
-
- Upload fails
|
|
303
|
-
- Wrong output
|
|
332
|
+
- Port not found → nff init to re-detect
|
|
333
|
+
- Upload fails → nff flash --manual-reset, hold BOOT when prompted
|
|
334
|
+
- Wrong output → nff monitor --baud 115200
|
|
304
335
|
"""
|
|
305
336
|
|
|
306
337
|
|
{nff-0.2.0 → nff-0.2.2}/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|