replx 1.6.7__tar.gz → 1.8__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 (152) hide show
  1. replx-1.8/PKG-INFO +110 -0
  2. replx-1.8/README.md +84 -0
  3. {replx-1.6.7 → replx-1.8}/pyproject.toml +1 -1
  4. {replx-1.6.7 → replx-1.8}/replx/__init__.py +1 -1
  5. {replx-1.6.7 → replx-1.8}/replx/cli/agent/server/handlers/repl.py +1 -2
  6. {replx-1.6.7 → replx-1.8}/replx/cli/app.py +4 -1
  7. {replx-1.6.7 → replx-1.8}/replx/cli/commands/adc.py +11 -1
  8. {replx-1.6.7 → replx-1.8}/replx/cli/commands/exec.py +123 -35
  9. {replx-1.6.7 → replx-1.8}/replx/cli/commands/gpio.py +12 -8
  10. {replx-1.6.7 → replx-1.8}/replx/cli/commands/i2c.py +158 -20
  11. {replx-1.6.7 → replx-1.8}/replx/cli/commands/pwm.py +1019 -1008
  12. {replx-1.6.7 → replx-1.8}/replx/cli/commands/spi.py +36 -31
  13. {replx-1.6.7 → replx-1.8}/replx/cli/commands/uart.py +30 -17
  14. {replx-1.6.7 → replx-1.8}/replx/protocol/repl.py +2 -5
  15. {replx-1.6.7 → replx-1.8}/replx/transport/__init__.py +1 -3
  16. {replx-1.6.7 → replx-1.8}/replx/transport/serial.py +2 -6
  17. replx-1.8/replx/typehints/comm_separate/EFR32MG/digi/__init__.pyi +11 -0
  18. replx-1.8/replx/typehints/comm_separate/EFR32MG/digi/ble.pyi +1078 -0
  19. replx-1.8/replx/typehints/comm_separate/EFR32MG/xbee/__init__.pyi +394 -0
  20. replx-1.8/replx/typehints/comm_separate/EFR32MG/xbee/modem_status.pyi +116 -0
  21. replx-1.8/replx/typehints/comm_separate/EFR32MG/xbee/relay.pyi +156 -0
  22. replx-1.8/replx.egg-info/PKG-INFO +110 -0
  23. {replx-1.6.7 → replx-1.8}/replx.egg-info/SOURCES.txt +6 -1
  24. replx-1.6.7/PKG-INFO +0 -25
  25. replx-1.6.7/replx/transport/base.py +0 -43
  26. replx-1.6.7/replx.egg-info/PKG-INFO +0 -25
  27. {replx-1.6.7 → replx-1.8}/LICENSE +0 -0
  28. {replx-1.6.7 → replx-1.8}/replx/cli/__init__.py +0 -0
  29. {replx-1.6.7 → replx-1.8}/replx/cli/agent/__init__.py +0 -0
  30. {replx-1.6.7 → replx-1.8}/replx/cli/agent/client/__init__.py +0 -0
  31. {replx-1.6.7 → replx-1.8}/replx/cli/agent/client/core.py +0 -0
  32. {replx-1.6.7 → replx-1.8}/replx/cli/agent/client/session.py +0 -0
  33. {replx-1.6.7 → replx-1.8}/replx/cli/agent/protocol.py +0 -0
  34. {replx-1.6.7 → replx-1.8}/replx/cli/agent/server/__init__.py +0 -0
  35. {replx-1.6.7 → replx-1.8}/replx/cli/agent/server/__main__.py +0 -0
  36. {replx-1.6.7 → replx-1.8}/replx/cli/agent/server/command_dispatcher.py +0 -0
  37. {replx-1.6.7 → replx-1.8}/replx/cli/agent/server/connection_manager.py +0 -0
  38. {replx-1.6.7 → replx-1.8}/replx/cli/agent/server/core.py +0 -0
  39. {replx-1.6.7 → replx-1.8}/replx/cli/agent/server/handlers/__init__.py +0 -0
  40. {replx-1.6.7 → replx-1.8}/replx/cli/agent/server/handlers/exec.py +0 -0
  41. {replx-1.6.7 → replx-1.8}/replx/cli/agent/server/handlers/filesystem.py +0 -0
  42. {replx-1.6.7 → replx-1.8}/replx/cli/agent/server/handlers/i2c.py +0 -0
  43. {replx-1.6.7 → replx-1.8}/replx/cli/agent/server/handlers/session.py +0 -0
  44. {replx-1.6.7 → replx-1.8}/replx/cli/agent/server/handlers/spi.py +0 -0
  45. {replx-1.6.7 → replx-1.8}/replx/cli/agent/server/handlers/transfer.py +0 -0
  46. {replx-1.6.7 → replx-1.8}/replx/cli/agent/server/handlers/uart.py +0 -0
  47. {replx-1.6.7 → replx-1.8}/replx/cli/agent/server/session_manager.py +0 -0
  48. {replx-1.6.7 → replx-1.8}/replx/cli/commands/__init__.py +0 -0
  49. {replx-1.6.7 → replx-1.8}/replx/cli/commands/device.py +0 -0
  50. {replx-1.6.7 → replx-1.8}/replx/cli/commands/file.py +0 -0
  51. {replx-1.6.7 → replx-1.8}/replx/cli/commands/firmware.py +0 -0
  52. {replx-1.6.7 → replx-1.8}/replx/cli/commands/package.py +0 -0
  53. {replx-1.6.7 → replx-1.8}/replx/cli/commands/utility.py +0 -0
  54. {replx-1.6.7 → replx-1.8}/replx/cli/config.py +0 -0
  55. {replx-1.6.7 → replx-1.8}/replx/cli/connection.py +0 -0
  56. {replx-1.6.7 → replx-1.8}/replx/cli/helpers/__init__.py +0 -0
  57. {replx-1.6.7 → replx-1.8}/replx/cli/helpers/compiler.py +0 -0
  58. {replx-1.6.7 → replx-1.8}/replx/cli/helpers/environment.py +0 -0
  59. {replx-1.6.7 → replx-1.8}/replx/cli/helpers/output.py +0 -0
  60. {replx-1.6.7 → replx-1.8}/replx/cli/helpers/registry.py +0 -0
  61. {replx-1.6.7 → replx-1.8}/replx/cli/helpers/scanner.py +0 -0
  62. {replx-1.6.7 → replx-1.8}/replx/cli/helpers/store.py +0 -0
  63. {replx-1.6.7 → replx-1.8}/replx/cli/helpers/updater.py +0 -0
  64. {replx-1.6.7 → replx-1.8}/replx/commands.py +0 -0
  65. {replx-1.6.7 → replx-1.8}/replx/protocol/__init__.py +0 -0
  66. {replx-1.6.7 → replx-1.8}/replx/protocol/storage.py +0 -0
  67. {replx-1.6.7 → replx-1.8}/replx/terminal.py +0 -0
  68. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/_thread.pyi +0 -0
  69. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/aioble/__init__.pyi +0 -0
  70. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/array.pyi +0 -0
  71. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/asyncio/__init__.pyi +0 -0
  72. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/binascii.pyi +0 -0
  73. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/bluetooth.pyi +0 -0
  74. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/builtins.pyi +0 -0
  75. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/cmath.pyi +0 -0
  76. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/collections.pyi +0 -0
  77. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/cryptolib.pyi +0 -0
  78. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/deflate.pyi +0 -0
  79. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/errno.pyi +0 -0
  80. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/framebuf.pyi +0 -0
  81. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/gc.pyi +0 -0
  82. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/hashlib.pyi +0 -0
  83. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/heapq.pyi +0 -0
  84. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/io.pyi +0 -0
  85. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/json.pyi +0 -0
  86. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/lwip.pyi +0 -0
  87. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/machine.pyi +0 -0
  88. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/math.pyi +0 -0
  89. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/micropython.pyi +0 -0
  90. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/mip/__init__.pyi +0 -0
  91. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/network.pyi +0 -0
  92. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/ntptime.pyi +0 -0
  93. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/os.pyi +0 -0
  94. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/platform.pyi +0 -0
  95. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/random.pyi +0 -0
  96. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/re.pyi +0 -0
  97. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/requests/__init__.pyi +0 -0
  98. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/select.pyi +0 -0
  99. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/socket.pyi +0 -0
  100. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/ssl.pyi +0 -0
  101. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/struct.pyi +0 -0
  102. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/sys.pyi +0 -0
  103. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/time.pyi +0 -0
  104. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/tls.pyi +0 -0
  105. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/uasyncio.pyi +0 -0
  106. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/uctypes.pyi +0 -0
  107. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/urequests.pyi +0 -0
  108. {replx-1.6.7 → replx-1.8}/replx/typehints/comm/vfs.pyi +0 -0
  109. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/binascii.pyi +0 -0
  110. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/errno.pyi +0 -0
  111. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/hashlib.pyi +0 -0
  112. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/io.pyi +0 -0
  113. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/json.pyi +0 -0
  114. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/machine.pyi +0 -0
  115. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/math.pyi +0 -0
  116. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/micropython.pyi +0 -0
  117. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/network.pyi +0 -0
  118. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/os.pyi +0 -0
  119. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/select.pyi +0 -0
  120. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/socket.pyi +0 -0
  121. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/ssl.pyi +0 -0
  122. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/struct.pyi +0 -0
  123. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/sys.pyi +0 -0
  124. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/time.pyi +0 -0
  125. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/ubinascii.pyi +0 -0
  126. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/ucryptolib.pyi +0 -0
  127. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/uerrno.pyi +0 -0
  128. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/uhashlib.pyi +0 -0
  129. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/uio.pyi +0 -0
  130. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/ujson.pyi +0 -0
  131. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/umachine.pyi +0 -0
  132. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/uos.pyi +0 -0
  133. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/uselect.pyi +0 -0
  134. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/usocket.pyi +0 -0
  135. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/ussl.pyi +0 -0
  136. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/ustruct.pyi +0 -0
  137. {replx-1.6.7 → replx-1.8}/replx/typehints/comm_separate/EFR32MG/utime.pyi +0 -0
  138. {replx-1.6.7 → replx-1.8}/replx/typehints/core/ESP32/aioespnow.pyi +0 -0
  139. {replx-1.6.7 → replx-1.8}/replx/typehints/core/ESP32/esp.pyi +0 -0
  140. {replx-1.6.7 → replx-1.8}/replx/typehints/core/ESP32/esp32.pyi +0 -0
  141. {replx-1.6.7 → replx-1.8}/replx/typehints/core/ESP32/espnow.pyi +0 -0
  142. {replx-1.6.7 → replx-1.8}/replx/typehints/core/MIMXRT1062DVJ6A/mimxrt.pyi +0 -0
  143. {replx-1.6.7 → replx-1.8}/replx/typehints/core/RP2350/rp2.pyi +0 -0
  144. {replx-1.6.7 → replx-1.8}/replx/utils/__init__.py +0 -0
  145. {replx-1.6.7 → replx-1.8}/replx/utils/constants.py +0 -0
  146. {replx-1.6.7 → replx-1.8}/replx/utils/device_info.py +0 -0
  147. {replx-1.6.7 → replx-1.8}/replx/utils/exceptions.py +0 -0
  148. {replx-1.6.7 → replx-1.8}/replx.egg-info/dependency_links.txt +0 -0
  149. {replx-1.6.7 → replx-1.8}/replx.egg-info/entry_points.txt +0 -0
  150. {replx-1.6.7 → replx-1.8}/replx.egg-info/requires.txt +0 -0
  151. {replx-1.6.7 → replx-1.8}/replx.egg-info/top_level.txt +0 -0
  152. {replx-1.6.7 → replx-1.8}/setup.cfg +0 -0
replx-1.8/PKG-INFO ADDED
@@ -0,0 +1,110 @@
1
+ Metadata-Version: 2.4
2
+ Name: replx
3
+ Version: 1.8
4
+ Summary: replx is a fast, modern MicroPython CLI: turbo REPL, robust file sync (put/get), project install, mpy-cross integration, and smart port discovery.
5
+ Author-email: "chanmin.park" <devcamp@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/PlanXLab/replx
8
+ Project-URL: Repository, https://github.com/PlanXLab/replx
9
+ Project-URL: Issues, https://github.com/PlanXLab/replx/issues
10
+ Keywords: micropython,repl,serial,pyserial,typer,mpy-cross,deploy,gpio,i2c,spi,uart,pwm,adc
11
+ Classifier: Environment :: Console
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Topic :: Software Development :: Embedded Systems
16
+ Classifier: Topic :: System :: Hardware :: Universal Serial Bus (USB)
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: typer>=0.12
21
+ Requires-Dist: rich>=13.0
22
+ Requires-Dist: pyserial>=3.5
23
+ Requires-Dist: mpy-cross>=1.26
24
+ Requires-Dist: psutil>=5.9.0
25
+ Dynamic: license-file
26
+
27
+ # replx
28
+
29
+ [![PyPI version](https://badge.fury.io/py/replx.svg)](https://badge.fury.io/py/replx)
30
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
31
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
32
+
33
+ `replx` is a CLI tool for MicroPython development. It uses an agent-based architecture to manage multiple CLI sessions and multiple boards in a consistent workflow.
34
+
35
+ ## What replx provides
36
+
37
+ - Shared connection management across terminal sessions
38
+ - Foreground and background board handling per session
39
+ - Workspace-level default device configuration
40
+ - File operations on device storage
41
+ - Script execution, REPL access, and utility commands
42
+
43
+ ## Installation
44
+
45
+ ```sh
46
+ pip install replx
47
+ ```
48
+
49
+ ## Command summary
50
+
51
+ ### Connection and session
52
+
53
+ - `setup`: Initialize workspace settings and register a default device.
54
+ - `scan`: List available serial devices.
55
+ - `status`: Show session and connection state.
56
+ - `fg`: Change the foreground device for the current session.
57
+ - `whoami`: Show the current foreground device.
58
+ - `disconnect`: Close a device connection.
59
+ - `shutdown`: Stop the agent and clear active sessions.
60
+
61
+ ### Execution and interaction
62
+
63
+ - `exec` (`-c`): Execute inline Python code on the device.
64
+ - `run`: Run a local or device-side script.
65
+ - `repl`: Open an interactive REPL session.
66
+ - `shell`: Open a device file-system shell.
67
+
68
+ ### File operations
69
+
70
+ - `ls`: List files and directories.
71
+ - `cat`: Print file content.
72
+ - `get`: Download files from device to local.
73
+ - `put`: Upload files from local to device.
74
+ - `cp`: Copy files or directories on device.
75
+ - `mv`: Move or rename files or directories on device.
76
+ - `rm`: Remove files or directories on device.
77
+ - `mkdir`: Create directories on device.
78
+ - `touch`: Create an empty file or update timestamps.
79
+
80
+ ### Device management
81
+
82
+ - `usage`: Show device storage usage.
83
+ - `reset`: Perform a soft reset.
84
+ - `format`: Format the device file system.
85
+ - `init`: Run initialization scripts on device.
86
+ - `wifi`: Manage Wi-Fi configuration and status.
87
+ - `firmware`: Check, download, or update firmware.
88
+
89
+ ### Package and build
90
+
91
+ - `pkg`: Search, download, and update packages.
92
+ - `mpy`: Compile `.py` files to `.mpy`.
93
+
94
+ ### Hardware
95
+
96
+ - `gpio`: Read, write, and run GPIO sequences.
97
+ - `pwm`: Generate and monitor PWM signals.
98
+ - `adc`: Read ADC pins and run a board-side scope UI.
99
+ - `uart`: Open, write, read, and monitor UART.
100
+ - `spi`: Open, write, read, and transfer SPI data.
101
+ - `i2c`: Scan, read, write, and dump I2C devices.
102
+
103
+ ## Notes
104
+
105
+ - `scan`, `status`, `whoami`, and `shutdown` are special commands and do not accept `--port`.
106
+ - Most device commands can omit the port when a foreground or workspace default device is available.
107
+
108
+ ## License
109
+
110
+ MIT
replx-1.8/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # replx
2
+
3
+ [![PyPI version](https://badge.fury.io/py/replx.svg)](https://badge.fury.io/py/replx)
4
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ `replx` is a CLI tool for MicroPython development. It uses an agent-based architecture to manage multiple CLI sessions and multiple boards in a consistent workflow.
8
+
9
+ ## What replx provides
10
+
11
+ - Shared connection management across terminal sessions
12
+ - Foreground and background board handling per session
13
+ - Workspace-level default device configuration
14
+ - File operations on device storage
15
+ - Script execution, REPL access, and utility commands
16
+
17
+ ## Installation
18
+
19
+ ```sh
20
+ pip install replx
21
+ ```
22
+
23
+ ## Command summary
24
+
25
+ ### Connection and session
26
+
27
+ - `setup`: Initialize workspace settings and register a default device.
28
+ - `scan`: List available serial devices.
29
+ - `status`: Show session and connection state.
30
+ - `fg`: Change the foreground device for the current session.
31
+ - `whoami`: Show the current foreground device.
32
+ - `disconnect`: Close a device connection.
33
+ - `shutdown`: Stop the agent and clear active sessions.
34
+
35
+ ### Execution and interaction
36
+
37
+ - `exec` (`-c`): Execute inline Python code on the device.
38
+ - `run`: Run a local or device-side script.
39
+ - `repl`: Open an interactive REPL session.
40
+ - `shell`: Open a device file-system shell.
41
+
42
+ ### File operations
43
+
44
+ - `ls`: List files and directories.
45
+ - `cat`: Print file content.
46
+ - `get`: Download files from device to local.
47
+ - `put`: Upload files from local to device.
48
+ - `cp`: Copy files or directories on device.
49
+ - `mv`: Move or rename files or directories on device.
50
+ - `rm`: Remove files or directories on device.
51
+ - `mkdir`: Create directories on device.
52
+ - `touch`: Create an empty file or update timestamps.
53
+
54
+ ### Device management
55
+
56
+ - `usage`: Show device storage usage.
57
+ - `reset`: Perform a soft reset.
58
+ - `format`: Format the device file system.
59
+ - `init`: Run initialization scripts on device.
60
+ - `wifi`: Manage Wi-Fi configuration and status.
61
+ - `firmware`: Check, download, or update firmware.
62
+
63
+ ### Package and build
64
+
65
+ - `pkg`: Search, download, and update packages.
66
+ - `mpy`: Compile `.py` files to `.mpy`.
67
+
68
+ ### Hardware
69
+
70
+ - `gpio`: Read, write, and run GPIO sequences.
71
+ - `pwm`: Generate and monitor PWM signals.
72
+ - `adc`: Read ADC pins and run a board-side scope UI.
73
+ - `uart`: Open, write, read, and monitor UART.
74
+ - `spi`: Open, write, read, and transfer SPI data.
75
+ - `i2c`: Scan, read, write, and dump I2C devices.
76
+
77
+ ## Notes
78
+
79
+ - `scan`, `status`, `whoami`, and `shutdown` are special commands and do not accept `--port`.
80
+ - Most device commands can omit the port when a foreground or workspace default device is available.
81
+
82
+ ## License
83
+
84
+ MIT
@@ -9,7 +9,7 @@ description = "replx is a fast, modern MicroPython CLI: turbo REPL, robust file
9
9
  readme = "README.md"
10
10
  authors = [{ name = "chanmin.park", email = "devcamp@gmail.com" }]
11
11
  requires-python = ">=3.10"
12
- keywords = ["micropython", "repl", "serial", "pyserial", "typer", "mpy-cross", "deploy"]
12
+ keywords = ["micropython", "repl", "serial", "pyserial", "typer", "mpy-cross", "deploy", "gpio", "i2c", "spi", "uart", "pwm", "adc"]
13
13
  license = "MIT"
14
14
  license-files = ["LICENSE"]
15
15
 
@@ -1,5 +1,5 @@
1
1
  __all__ = ["__version__", "get_version", "__description__"]
2
- __version__ = "1.6.7"
2
+ __version__ = "1.8"
3
3
  __description__ = "Fast, modern MicroPython CLI with REPL, file sync, install, and smart port detection."
4
4
  __author__ = "PlanX Lab Development Team"
5
5
 
@@ -35,8 +35,7 @@ class ReplCommandsMixin:
35
35
  while conn.repl.active and conn.repl_protocol:
36
36
  try:
37
37
  data = await loop.run_in_executor(
38
- self._slow_executor,
39
- self._read_repl_chunk, repl
38
+ self._slow_executor, self._read_repl_chunk, repl
40
39
  )
41
40
  if data:
42
41
  conn.repl.append_output(data)
@@ -353,9 +353,12 @@ def _print_main_help():
353
353
  ("mpy", "Compile Python files to .mpy bytecode"),
354
354
  ]),
355
355
  ("Hardware", [
356
- ("i2c", "Scan, read, write, dump I2C devices on the connected board"),
357
356
  ("gpio", "Read, write, and run GPIO sequences on the connected board"),
357
+ ("pwm", "Generate and monitor PWM signals on the connected board"),
358
358
  ("adc", "Read ADC pins and run the board-side ADC scope UI"),
359
+ ("uart", "Open, write, read, monitor UART on the connected board"),
360
+ ("spi", "Open, write, read, xfer SPI devices on the connected board"),
361
+ ("i2c", "Scan, read, write, dump I2C devices on the connected board"),
359
362
  ]),
360
363
  ]
361
364
 
@@ -1145,9 +1145,19 @@ def adc_cmd(
1145
1145
  sample: int = typer.Option(10, "--sample", metavar="MS", help="Initial scope render rate"),
1146
1146
  show_help: bool = typer.Option(False, "--help", "-h", is_eager=True, hidden=True),
1147
1147
  ):
1148
- if show_help or not args:
1148
+ if show_help:
1149
1149
  _print_adc_help()
1150
1150
  raise typer.Exit()
1151
+ if not args:
1152
+ OutputHelper.print_panel(
1153
+ "Subcommands: [bright_blue]read[/bright_blue] [bright_blue]scope[/bright_blue]\n\n"
1154
+ " [bright_green]replx PORT adc read GP26[/bright_green]\n"
1155
+ " [bright_green]replx PORT adc scope GP26 GP27 GP28[/bright_green]\n\n"
1156
+ "Use [bright_blue]replx adc --help[/bright_blue] for details.",
1157
+ title="ADC",
1158
+ border_style="yellow",
1159
+ )
1160
+ raise typer.Exit(1)
1151
1161
 
1152
1162
  subcmd = args[0].lower()
1153
1163
  pos_args = args[1:]
@@ -10,6 +10,7 @@ import shlex
10
10
  import hashlib
11
11
  import subprocess
12
12
  import shutil
13
+ from collections import deque
13
14
  from pathlib import Path
14
15
 
15
16
  import typer
@@ -73,6 +74,29 @@ def _wrap_trailing_expression_for_print(code: str) -> str:
73
74
  return code
74
75
 
75
76
 
77
+ def _is_repl_enter_key(ch: bytes) -> bool:
78
+ return ch in (b"\r", b"\n", b"\r\n", b"\n\r")
79
+
80
+
81
+ def _setup_readline_for_repl():
82
+ try:
83
+ import readline
84
+ except ImportError:
85
+ return None
86
+
87
+ try:
88
+ readline.parse_and_bind("set editing-mode emacs")
89
+ except Exception:
90
+ pass
91
+
92
+ try:
93
+ readline.set_auto_history(False)
94
+ except Exception:
95
+ pass
96
+
97
+ return readline
98
+
99
+
76
100
  def _resolve_vscode_command() -> list[str] | None:
77
101
  candidates = ["code", "code-insiders", "codium"]
78
102
  comspec = os.environ.get("COMSPEC", "cmd.exe")
@@ -1355,6 +1379,45 @@ Start a live MicroPython session where you can type code line by line.
1355
1379
 
1356
1380
  repl_running = [True]
1357
1381
  reader_client = [None]
1382
+ pending_echoes = deque()
1383
+ pending_echo_lock = threading.Lock()
1384
+ pending_output_fragment = [""]
1385
+
1386
+ def _queue_pending_echo(line: str) -> None:
1387
+ with pending_echo_lock:
1388
+ pending_echoes.append(line + "\r\n")
1389
+
1390
+ def _strip_pending_echo(output: str) -> str:
1391
+ if not output:
1392
+ return output
1393
+
1394
+ with pending_echo_lock:
1395
+ text = pending_output_fragment[0] + output
1396
+ normalized = text.replace("\r\n", "\n")
1397
+ pending_output_fragment[0] = ""
1398
+
1399
+ if not normalized:
1400
+ return ""
1401
+
1402
+ trailing_fragment = "" if normalized.endswith("\n") else normalized.split("\n")[-1]
1403
+ lines = normalized.splitlines(keepends=True)
1404
+ if trailing_fragment and lines:
1405
+ lines = lines[:-1]
1406
+
1407
+ kept: list[str] = []
1408
+ for line in lines:
1409
+ if pending_echoes and line == pending_echoes[0].replace("\r\n", "\n"):
1410
+ pending_echoes.popleft()
1411
+ continue
1412
+ kept.append(line)
1413
+
1414
+ if trailing_fragment:
1415
+ if pending_echoes and pending_echoes[0].replace("\r\n", "\n").startswith(trailing_fragment):
1416
+ pending_output_fragment[0] = trailing_fragment
1417
+ else:
1418
+ kept.append(trailing_fragment)
1419
+
1420
+ return "".join(kept)
1358
1421
 
1359
1422
  def reader_thread_func():
1360
1423
  try:
@@ -1366,6 +1429,10 @@ Start a live MicroPython session where you can type code line by line.
1366
1429
  output = result.get('output', '')
1367
1430
  if output:
1368
1431
  sleep_time = 0.005
1432
+ if sys.platform == 'darwin':
1433
+ output = _strip_pending_echo(output)
1434
+ if not output:
1435
+ continue
1369
1436
  output = colorize_prompt(output)
1370
1437
  if IS_WINDOWS:
1371
1438
  sys.stdout.buffer.write(output.encode('utf-8').replace(b'\r', b''))
@@ -1390,6 +1457,7 @@ Start a live MicroPython session where you can type code line by line.
1390
1457
  reader.start()
1391
1458
 
1392
1459
  writer_client = _create_agent_client()
1460
+ repl_enter_data = '\r'
1393
1461
 
1394
1462
  _old_console_mode = disable_quick_edit_mode()
1395
1463
 
@@ -1430,53 +1498,73 @@ Start a live MicroPython session where you can type code line by line.
1430
1498
  _atexit.register(_restore_console)
1431
1499
 
1432
1500
  input_buffer = ""
1433
-
1501
+
1434
1502
  try:
1435
- while True:
1436
- char = _repl_getch()
1437
-
1438
- if char == b'\x00' or not char:
1439
- continue
1440
-
1441
- if char == CTRL_D:
1442
- input_buffer = ""
1503
+ if sys.platform == 'darwin':
1504
+ readline_mod = _setup_readline_for_repl()
1505
+ while True:
1443
1506
  try:
1444
- writer_client.send_command('repl_write', data='\x04', timeout=1.5, max_retries=1)
1445
- except Exception:
1507
+ line = input()
1508
+ except EOFError:
1446
1509
  break
1447
- continue
1448
-
1449
- if char in (b'\r', b'\n'):
1450
- if input_buffer.strip() == 'exit()':
1451
- repl_running[0] = False
1452
- reader.join(timeout=0.3)
1510
+ except KeyboardInterrupt:
1453
1511
  try:
1454
1512
  writer_client.send_command('repl_write', data='\x03', timeout=1.5, max_retries=1)
1455
1513
  except Exception:
1456
- pass
1514
+ break
1515
+ continue
1516
+
1517
+ if line.strip() == 'exit()':
1457
1518
  break
1458
- input_buffer = ""
1519
+
1459
1520
  try:
1460
- writer_client.send_command('repl_write', data='\r', timeout=1.5, max_retries=1)
1521
+ if readline_mod and line and (readline_mod.get_current_history_length() == 0 or readline_mod.get_history_item(readline_mod.get_current_history_length()) != line):
1522
+ readline_mod.add_history(line)
1523
+ _queue_pending_echo(line)
1524
+ writer_client.send_command('repl_write', data=line + repl_enter_data, timeout=1.5, max_retries=1)
1461
1525
  except Exception:
1462
1526
  break
1463
- elif char == b'\x7f' or char == b'\x08':
1464
- if input_buffer:
1465
- input_buffer = input_buffer[:-1]
1466
- try:
1467
- writer_client.send_command('repl_write', data=char.decode('utf-8', errors='replace'), timeout=1.5, max_retries=1)
1468
- except Exception:
1527
+ else:
1528
+ while True:
1529
+ char = getch()
1530
+
1531
+ if char == b'\x00' or not char:
1532
+ continue
1533
+
1534
+ if char == CTRL_D:
1469
1535
  break
1470
- else:
1471
- if char >= b' ':
1536
+
1537
+ if _is_repl_enter_key(char):
1538
+ if input_buffer.strip() == 'exit()':
1539
+ repl_running[0] = False
1540
+ reader.join(timeout=0.3)
1541
+ try:
1542
+ writer_client.send_command('repl_write', data='\x03', timeout=1.5, max_retries=1)
1543
+ except Exception:
1544
+ pass
1545
+ break
1546
+ input_buffer = ""
1472
1547
  try:
1473
- input_buffer += char.decode('utf-8', errors='ignore')
1474
- except UnicodeDecodeError:
1475
- pass
1476
- try:
1477
- writer_client.send_command('repl_write', data=char.decode('utf-8', errors='replace'), timeout=1.5, max_retries=1)
1478
- except Exception:
1479
- break
1548
+ writer_client.send_command('repl_write', data=repl_enter_data, timeout=1.5, max_retries=1)
1549
+ except Exception:
1550
+ break
1551
+ elif char == b'\x7f' or char == b'\x08':
1552
+ if input_buffer:
1553
+ input_buffer = input_buffer[:-1]
1554
+ try:
1555
+ writer_client.send_command('repl_write', data=char.decode('utf-8', errors='replace'), timeout=1.5, max_retries=1)
1556
+ except Exception:
1557
+ break
1558
+ else:
1559
+ if char >= b' ':
1560
+ try:
1561
+ input_buffer += char.decode('utf-8', errors='ignore')
1562
+ except UnicodeDecodeError:
1563
+ pass
1564
+ try:
1565
+ writer_client.send_command('repl_write', data=char.decode('utf-8', errors='replace'), timeout=1.5, max_retries=1)
1566
+ except Exception:
1567
+ break
1480
1568
 
1481
1569
  except KeyboardInterrupt:
1482
1570
  pass
@@ -263,7 +263,6 @@ _next_level = bytearray(1)
263
263
 
264
264
 
265
265
  def _isr(pin):
266
- """Hard-IRQ handler: zero heap allocation."""
267
266
  global _wr, _overflow
268
267
  nxt = (_wr + 1) % _BUF_SIZE
269
268
  if nxt == _rd:
@@ -276,7 +275,6 @@ def _isr(pin):
276
275
 
277
276
 
278
277
  def _flush_buf():
279
- """Drain all pending edges into a plain list (called from main loop only)."""
280
278
  global _rd, _overflow
281
279
  edges = []
282
280
  while _rd != _wr:
@@ -307,7 +305,6 @@ def _make_plot():
307
305
 
308
306
 
309
307
  def _poll_key():
310
- """Return the first pending character, or None. Raises KeyboardInterrupt on Ctrl+C."""
311
308
  r, _, _ = _sel.select([sys.stdin], [], [], 0)
312
309
  if not r:
313
310
  return None
@@ -318,7 +315,6 @@ def _poll_key():
318
315
 
319
316
 
320
317
  def _draw_log(w, log_buf, log_pos):
321
- """Redraw all 8 edge-log rows (oldest at top, newest at bottom)."""
322
318
  for i in range(_LOG_N):
323
319
  idx = (log_pos - _LOG_N + i) % _LOG_N
324
320
  entry = log_buf[idx]
@@ -394,10 +390,8 @@ def main():
394
390
 
395
391
  t_now = time.ticks_us()
396
392
  held = time.ticks_diff(t_now, last_change_t)
397
- # Update last known level from IRQ edge history (avoids pin.value() race)
398
393
  if edges:
399
394
  last_irq_level = edges[-1][1]
400
- # Advance scope exactly one tick per render loop with correct level
401
395
  _pt = sc._t
402
396
  sc.tick([float(last_irq_level)])
403
397
  if sc._t <= _pt:
@@ -468,7 +462,6 @@ def _make_write_code(pin_no: int, pin_name: str, value: int) -> str:
468
462
 
469
463
 
470
464
  def _mk_timeout_exit(action: str, read_pin_name: str, indent: int = 12) -> str:
471
- """Generate the MicroPython timeout-exit snippet (used inside while loops)."""
472
465
  pad = " " * indent
473
466
  return (
474
467
  f"{pad}res['read_action']={action!r}\n"
@@ -1178,9 +1171,20 @@ def gpio_cmd(
1178
1171
  repeat: int = typer.Option(1, "--repeat", metavar="N", help="Repeat gpio read N times (0=infinite until Ctrl+C, default 1)"),
1179
1172
  show_help: bool = typer.Option(False, "--help", "-h", is_eager=True, hidden=True),
1180
1173
  ):
1181
- if show_help or not args:
1174
+ if show_help:
1182
1175
  _print_gpio_help()
1183
1176
  raise typer.Exit()
1177
+ if not args:
1178
+ OutputHelper.print_panel(
1179
+ "Subcommands: [bright_blue]monitor[/bright_blue] [bright_blue]read[/bright_blue] [bright_blue]write[/bright_blue] [bright_blue]seq[/bright_blue]\n\n"
1180
+ " [bright_green]replx PORT gpio read GP1[/bright_green]\n"
1181
+ " [bright_green]replx PORT gpio write GP25 1[/bright_green]\n"
1182
+ " [bright_green]replx PORT gpio monitor GP1[/bright_green]\n\n"
1183
+ "Use [bright_blue]replx gpio --help[/bright_blue] for details.",
1184
+ title="GPIO",
1185
+ border_style="yellow",
1186
+ )
1187
+ raise typer.Exit(1)
1184
1188
 
1185
1189
  subcmd = args[0].lower()
1186
1190
  pos_args = args[1:]