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.
Files changed (36) hide show
  1. {nff-0.2.0 → nff-0.2.2}/PKG-INFO +1 -1
  2. {nff-0.2.0 → nff-0.2.2}/nff/cli.py +2 -0
  3. nff-0.2.2/nff/commands/clean.py +117 -0
  4. {nff-0.2.0 → nff-0.2.2}/nff/commands/init.py +66 -35
  5. {nff-0.2.0 → nff-0.2.2}/nff.egg-info/PKG-INFO +1 -1
  6. {nff-0.2.0 → nff-0.2.2}/nff.egg-info/SOURCES.txt +1 -0
  7. {nff-0.2.0 → nff-0.2.2}/pyproject.toml +1 -1
  8. {nff-0.2.0 → nff-0.2.2}/LICENSE +0 -0
  9. {nff-0.2.0 → nff-0.2.2}/README.md +0 -0
  10. {nff-0.2.0 → nff-0.2.2}/nff/__init__.py +0 -0
  11. {nff-0.2.0 → nff-0.2.2}/nff/commands/__init__.py +0 -0
  12. {nff-0.2.0 → nff-0.2.2}/nff/commands/doctor.py +0 -0
  13. {nff-0.2.0 → nff-0.2.2}/nff/commands/flash.py +0 -0
  14. {nff-0.2.0 → nff-0.2.2}/nff/commands/monitor.py +0 -0
  15. {nff-0.2.0 → nff-0.2.2}/nff/commands/wokwi.py +0 -0
  16. {nff-0.2.0 → nff-0.2.2}/nff/config.py +0 -0
  17. {nff-0.2.0 → nff-0.2.2}/nff/mcp_server.py +0 -0
  18. {nff-0.2.0 → nff-0.2.2}/nff/tools/__init__.py +0 -0
  19. {nff-0.2.0 → nff-0.2.2}/nff/tools/boards.py +0 -0
  20. {nff-0.2.0 → nff-0.2.2}/nff/tools/installer.py +0 -0
  21. {nff-0.2.0 → nff-0.2.2}/nff/tools/serial.py +0 -0
  22. {nff-0.2.0 → nff-0.2.2}/nff/tools/toolchain.py +0 -0
  23. {nff-0.2.0 → nff-0.2.2}/nff/tools/wokwi.py +0 -0
  24. {nff-0.2.0 → nff-0.2.2}/nff/tools/wokwi_installer.py +0 -0
  25. {nff-0.2.0 → nff-0.2.2}/nff.egg-info/dependency_links.txt +0 -0
  26. {nff-0.2.0 → nff-0.2.2}/nff.egg-info/entry_points.txt +0 -0
  27. {nff-0.2.0 → nff-0.2.2}/nff.egg-info/requires.txt +0 -0
  28. {nff-0.2.0 → nff-0.2.2}/nff.egg-info/top_level.txt +0 -0
  29. {nff-0.2.0 → nff-0.2.2}/setup.cfg +0 -0
  30. {nff-0.2.0 → nff-0.2.2}/tests/test_boards.py +0 -0
  31. {nff-0.2.0 → nff-0.2.2}/tests/test_config.py +0 -0
  32. {nff-0.2.0 → nff-0.2.2}/tests/test_doctor.py +0 -0
  33. {nff-0.2.0 → nff-0.2.2}/tests/test_mcp.py +0 -0
  34. {nff-0.2.0 → nff-0.2.2}/tests/test_serial.py +0 -0
  35. {nff-0.2.0 → nff-0.2.2}/tests/test_toolchain.py +0 -0
  36. {nff-0.2.0 → nff-0.2.2}/tests/test_wokwi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nff
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Claude Code IoT Bridge — connect Claude to hardware via USB
5
5
  Author-email: Gauthier Lechevalier <gauthier.lechevalier26@gmail.com>
6
6
  License-Expression: MIT
@@ -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", "nff", "mcp"],
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 via MCP tool
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 (MCP tool, headless)
234
- 1. wokwi_flash(code, board="{fqbn}") — compile + simulate, returns serial_output
235
- 2. Inspect serial_output — no USB cable required
236
- 3. Iterate until output is correct
237
- 4. flash(code) — upload to real hardware when ready
238
-
239
- ### Full pipeline (visual, with circuit)
240
- 1. Write sketch to sketches/<name>/<name>.ino
241
- 2. Edit diagram.json to add components (see below)
242
- 3. nff flash --sim sketches/<name> --board {fqbn}
243
- 4. nff wokwi run --gui ← opens VS Code with animated circuit
244
-
245
- wokwi.toml must point to the compiled ELF:
246
- firmware = "sketches/<name>/build/{fqbn_dotted}/<name>.elf/<name>.ino.elf"
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 → fix sketch, re-run nff flash --sim
295
- - Wrong output → nff wokwi run --serial-log out.txt, inspect out.txt
296
- - Component silent → check diagram.json pin names and connection direction
297
- - Servo wrong angle→ verify duty values match the 500–2500 µs Wokwi range
298
- - Button skipped → ensure INPUT_PULLUP and wiring gpio→btn:1.l, GND→btn:2.l
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 → nff init to re-detect
302
- - Upload fails → nff flash --manual-reset, hold BOOT when prompted
303
- - Wrong output → nff monitor --baud 115200
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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nff
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Claude Code IoT Bridge — connect Claude to hardware via USB
5
5
  Author-email: Gauthier Lechevalier <gauthier.lechevalier26@gmail.com>
6
6
  License-Expression: MIT
@@ -12,6 +12,7 @@ nff.egg-info/entry_points.txt
12
12
  nff.egg-info/requires.txt
13
13
  nff.egg-info/top_level.txt
14
14
  nff/commands/__init__.py
15
+ nff/commands/clean.py
15
16
  nff/commands/doctor.py
16
17
  nff/commands/flash.py
17
18
  nff/commands/init.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "nff"
7
- version = "0.2.0"
7
+ version = "0.2.2"
8
8
  description = "Claude Code IoT Bridge — connect Claude to hardware via USB"
9
9
  readme = "README.md"
10
10
  license = "MIT"
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