termapy 0.76.0__tar.gz → 0.77.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.
Files changed (381) hide show
  1. {termapy-0.76.0 → termapy-0.77.0}/PKG-INFO +101 -160
  2. {termapy-0.76.0 → termapy-0.77.0}/README.md +99 -159
  3. termapy-0.76.0/pyproject.toml.orig → termapy-0.77.0/pyproject.toml +21 -11
  4. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/app.py +402 -393
  5. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/app_dirs.py +15 -15
  6. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/app_hooks.py +102 -120
  7. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/_cfg_icon.py +36 -8
  8. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/_run_record.py +2 -3
  9. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/_xmodem_handlers.py +2 -0
  10. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/_ymodem_handlers.py +5 -4
  11. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/app.py +23 -34
  12. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/cap.py +69 -33
  13. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/cfg.py +179 -81
  14. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/cls.py +1 -1
  15. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/credits.py +1 -1
  16. termapy-0.77.0/src/termapy/builtins/commands/dev.py +504 -0
  17. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/edit.py +103 -69
  18. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/env.py +19 -16
  19. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/exit.py +1 -1
  20. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/find.py +1 -1
  21. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/grep.py +1 -1
  22. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/help.py +173 -123
  23. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/log.py +3 -1
  24. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/mcp.py +54 -27
  25. termapy-0.77.0/src/termapy/builtins/commands/mem.py +1195 -0
  26. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/os_cmd.py +3 -3
  27. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/ping.py +2 -2
  28. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/plugin.py +6 -2
  29. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/port.py +64 -47
  30. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/profile.py +38 -20
  31. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/proto.py +427 -265
  32. termapy-0.77.0/src/termapy/builtins/commands/raw.py +54 -0
  33. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/run.py +37 -54
  34. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/search.py +21 -21
  35. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/seq.py +12 -11
  36. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/show.py +2 -2
  37. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/ss.py +5 -3
  38. termapy-0.77.0/src/termapy/builtins/commands/sym.py +464 -0
  39. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/term.py +142 -69
  40. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/var.py +22 -8
  41. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/xfer.py +14 -15
  42. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/demo.cfg +2 -1
  43. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/demo.profile.json +7 -0
  44. termapy-0.77.0/src/termapy/builtins/demo/demo.symbols.json +23 -0
  45. termapy-0.77.0/src/termapy/builtins/demo/demo_legacy.profile.json +30 -0
  46. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/plugin/cmd.py +1 -1
  47. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/plugin/probe.py +2 -2
  48. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/plugin/temp_plot.py +3 -3
  49. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/plugin/traffic.py +29 -23
  50. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/capture.py +121 -67
  51. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/capture_view.py +12 -5
  52. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/cli.py +96 -92
  53. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/cli_flags.py +34 -32
  54. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/config.py +422 -104
  55. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/config_resolve.py +11 -15
  56. termapy-0.77.0/src/termapy/converters.py +144 -0
  57. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/defaults.py +22 -12
  58. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/demo.py +242 -26
  59. termapy-0.77.0/src/termapy/devices/__init__.py +1085 -0
  60. termapy-0.77.0/src/termapy/devices/converters/__init__.py +34 -0
  61. termapy-0.77.0/src/termapy/devices/converters/svd.py +433 -0
  62. termapy-0.77.0/src/termapy/dialogs/_common.py +215 -0
  63. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/dialogs/cfg_confirm.py +1 -0
  64. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/dialogs/config_editor.py +72 -54
  65. termapy-0.77.0/src/termapy/dialogs/config_picker.py +367 -0
  66. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/dialogs/confirm_dialog.py +1 -1
  67. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/dialogs/filename_dialog.py +1 -1
  68. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/dialogs/proto_editor.py +5 -3
  69. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/dialogs/proto_picker.py +5 -5
  70. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/dialogs/script_editor.py +5 -3
  71. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/dialogs/script_picker.py +6 -6
  72. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/dialogs/update_available.py +1 -1
  73. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/dialogs/welcome_dialog.py +2 -2
  74. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/entry.py +35 -1
  75. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/env_flags.py +73 -17
  76. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/folder_ops.py +117 -43
  77. termapy-0.77.0/src/termapy/folders.py +239 -0
  78. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/authoring-profiles.md +15 -0
  79. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/cli.md +18 -9
  80. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/commands.md +80 -25
  81. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/config.md +34 -10
  82. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/demo.md +3 -1
  83. termapy-0.77.0/src/termapy/help/devices.md +140 -0
  84. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/environment.md +13 -2
  85. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/getting-started.md +20 -3
  86. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/index.md +3 -0
  87. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/mcp-server.md +5 -3
  88. termapy-0.77.0/src/termapy/help/memory.md +304 -0
  89. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/protocol-testing.md +77 -18
  90. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/scripting.md +23 -5
  91. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/serial-tools.md +13 -2
  92. termapy-0.77.0/src/termapy/help/symbols.md +283 -0
  93. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/toolbar.md +10 -5
  94. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/using-git.md +9 -0
  95. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/variables.md +10 -6
  96. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/writing-plugins.md +157 -27
  97. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help_dynamic.py +14 -2
  98. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help_tooltip.py +8 -7
  99. termapy-0.77.0/src/termapy/history_nav.py +128 -0
  100. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/404.html +99 -17
  101. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/acknowledgments.html +100 -18
  102. termapy-0.77.0/src/termapy/html/assets/javascripts/bundle.a2388154.min.js +10 -0
  103. termapy-0.77.0/src/termapy/html/assets/javascripts/workers/search.c47bb847.min.js +1 -0
  104. termapy-0.77.0/src/termapy/html/assets/stylesheets/classic/main.68ef4dbf.min.css +6 -0
  105. termapy-0.77.0/src/termapy/html/assets/stylesheets/classic/palette.813d4b3f.min.css +4 -0
  106. termapy-0.77.0/src/termapy/html/assets/stylesheets/modern/main.e442bf79.min.css +6 -0
  107. termapy-0.77.0/src/termapy/html/assets/stylesheets/modern/palette.b60ce83d.min.css +4 -0
  108. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/authoring-profiles.html +199 -84
  109. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/cli.html +119 -29
  110. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/commands.html +306 -47
  111. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/config.html +178 -50
  112. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/custom-buttons.html +101 -19
  113. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/data-capture.html +104 -22
  114. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/demo.html +105 -21
  115. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/device-help.html +101 -19
  116. termapy-0.77.0/src/termapy/html/devices.html +1363 -0
  117. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/environment.html +130 -36
  118. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/file-transfer.html +104 -22
  119. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/getting-started.html +128 -30
  120. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/index.html +104 -19
  121. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/installation.html +100 -18
  122. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/mcp-server.html +109 -22
  123. termapy-0.77.0/src/termapy/html/memory.html +1620 -0
  124. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/on-ai-assistance.html +101 -19
  125. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/ports.html +101 -19
  126. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/protocol-testing.html +217 -51
  127. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/scripting.html +121 -23
  128. termapy-0.77.0/src/termapy/html/search.json +1 -0
  129. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/serial-tools.html +113 -21
  130. termapy-0.77.0/src/termapy/html/symbols.html +1788 -0
  131. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/toolbar.html +110 -24
  132. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/using-git.html +122 -32
  133. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/variables.html +110 -24
  134. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/vt100.html +101 -19
  135. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/writing-plugins.html +432 -178
  136. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/legacy.py +5 -7
  137. termapy-0.77.0/src/termapy/markup.py +95 -0
  138. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/mcp/catalog.py +10 -0
  139. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/mcp/server.py +206 -116
  140. termapy-0.77.0/src/termapy/memory.py +862 -0
  141. termapy-0.77.0/src/termapy/memory_views.py +298 -0
  142. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/migration.py +54 -5
  143. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/pickers.py +22 -11
  144. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/plugins/__init__.py +11 -0
  145. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/plugins/capabilities.py +109 -10
  146. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/plugins/command.py +162 -6
  147. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/plugins/context.py +33 -13
  148. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/plugins/handles/fs.py +2 -2
  149. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/plugins/handles/internal.py +27 -1
  150. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/plugins/handles/io.py +5 -4
  151. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/plugins/handles/serial.py +5 -2
  152. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/plugins/handles/ui.py +2 -2
  153. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/plugins/loader.py +94 -23
  154. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/plugins/output_levels.py +30 -25
  155. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/plugins/params.py +19 -2
  156. termapy-0.77.0/src/termapy/plugins/views.py +170 -0
  157. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/port_control.py +131 -109
  158. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/port_format.py +24 -11
  159. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/profile/loader.py +9 -0
  160. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/profile/schema.json +20 -0
  161. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/profile/types.py +12 -3
  162. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/proto_debug.py +72 -64
  163. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/protocol/__init__.py +6 -0
  164. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/protocol/core.py +423 -133
  165. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/protocol/runner.py +152 -79
  166. termapy-0.77.0/src/termapy/protocol/type_codes.py +71 -0
  167. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/repl.py +653 -138
  168. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/request_response.py +4 -2
  169. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/response_parsers.py +6 -2
  170. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/run_docstring.py +8 -1
  171. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/run_legacy.py +13 -16
  172. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/run_profile_hooks.py +9 -17
  173. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/scripting.py +100 -34
  174. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/serial_engine.py +175 -109
  175. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/serial_port.py +117 -26
  176. termapy-0.77.0/src/termapy/symbols/__init__.py +99 -0
  177. termapy-0.77.0/src/termapy/symbols/address.py +226 -0
  178. termapy-0.77.0/src/termapy/symbols/converters/__init__.py +61 -0
  179. termapy-0.77.0/src/termapy/symbols/converters/xc32.py +227 -0
  180. termapy-0.77.0/src/termapy/symbols/format.py +266 -0
  181. termapy-0.77.0/src/termapy/symbols/provenance.py +193 -0
  182. termapy-0.77.0/src/termapy/symbols/session.py +226 -0
  183. termapy-0.77.0/src/termapy/symbols/table.py +552 -0
  184. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/terminal_host.py +94 -37
  185. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/title_bar.py +7 -5
  186. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/tree_render.py +7 -6
  187. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/widgets/status_bar.py +3 -1
  188. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/widgets/suggester.py +26 -3
  189. termapy-0.76.0/pyproject.toml +0 -138
  190. termapy-0.76.0/src/termapy/builtins/commands/run_edit.py +0 -40
  191. termapy-0.76.0/src/termapy/dialogs/_common.py +0 -168
  192. termapy-0.76.0/src/termapy/dialogs/config_picker.py +0 -228
  193. termapy-0.76.0/src/termapy/folders.py +0 -107
  194. termapy-0.76.0/src/termapy/history_nav.py +0 -68
  195. termapy-0.76.0/src/termapy/html/assets/javascripts/bundle.49251538.min.js +0 -10
  196. termapy-0.76.0/src/termapy/html/assets/javascripts/workers/search.7d14d953.min.js +0 -1
  197. termapy-0.76.0/src/termapy/html/assets/stylesheets/classic/main.38807bfb.min.css +0 -1
  198. termapy-0.76.0/src/termapy/html/assets/stylesheets/classic/palette.c7ab7c71.min.css +0 -1
  199. termapy-0.76.0/src/termapy/html/assets/stylesheets/modern/main.5da3a30f.min.css +0 -1
  200. termapy-0.76.0/src/termapy/html/assets/stylesheets/modern/palette.812a03bb.min.css +0 -1
  201. termapy-0.76.0/src/termapy/html/objects.inv +0 -0
  202. termapy-0.76.0/src/termapy/html/search.json +0 -1
  203. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/__init__.py +0 -0
  204. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/__main__.py +0 -0
  205. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/__init__.py +0 -0
  206. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/_crc_verbs.py +0 -0
  207. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/confirm.py +0 -0
  208. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/print.py +0 -0
  209. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/repeat.py +0 -0
  210. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/commands/stop.py +0 -0
  211. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/crc/__init__.py +0 -0
  212. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/crc/sum16.py +0 -0
  213. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/crc/sum8.py +0 -0
  214. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/.gitignore +0 -0
  215. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/__init__.py +0 -0
  216. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/demo_ndjson.profile.json +0 -0
  217. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/plugin/__init__.py +0 -0
  218. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/proto/at_test.pro +0 -0
  219. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/proto/bitfield_inline.pro +0 -0
  220. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/proto/modbus_inline.pro +0 -0
  221. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/run/at_demo.run +0 -0
  222. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/run/crc_tour.run +0 -0
  223. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/run/doc_screenshots.run +0 -0
  224. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/run/expect_test.run +0 -0
  225. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/run/gps_demo.run +0 -0
  226. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/run/smoke_test.run +0 -0
  227. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/run/status_check.run +0 -0
  228. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/run/var_demo.run +0 -0
  229. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/demo/run/welcome.run +0 -0
  230. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/viz/__init__.py +0 -0
  231. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/viz/hex_view.py +0 -0
  232. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/viz/modbus_view.py.old +0 -0
  233. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/builtins/viz/text_view.py +0 -0
  234. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/credits_data.py +0 -0
  235. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/demo_ndjson.py +0 -0
  236. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/demo_vt100.py +0 -0
  237. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/dialogs/__init__.py +0 -0
  238. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/dialogs/name_picker.py +0 -0
  239. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/dialogs/port_picker.py +0 -0
  240. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/dialogs/quick_setup.py +0 -0
  241. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/dialogs/set_var_dialog.py +0 -0
  242. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/.coverage +0 -0
  243. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/acknowledgments.md +0 -0
  244. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/custom-buttons.md +0 -0
  245. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/data-capture.md +0 -0
  246. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/device-help.md +0 -0
  247. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/file-transfer.md +0 -0
  248. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_01_main_tui.svg +0 -0
  249. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_02_help.svg +0 -0
  250. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_03_help_proto.svg +0 -0
  251. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_04_proto_send.svg +0 -0
  252. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_05_proto_crc.svg +0 -0
  253. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_06_proto_delay.svg +0 -0
  254. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_07_crc_python.svg +0 -0
  255. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_08_variables.svg +0 -0
  256. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_09_target_help.svg +0 -0
  257. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_10_config_info.svg +0 -0
  258. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_11_proto_run.svg +0 -0
  259. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_12_cap_struct.svg +0 -0
  260. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_13_cap_progress.svg +0 -0
  261. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_14_port_list.svg +0 -0
  262. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_15_xfer.svg +0 -0
  263. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_16_toolbar.svg +0 -0
  264. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_17_proto_debug.svg +0 -0
  265. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_20_palette.svg +0 -0
  266. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_21_script_picker.svg +0 -0
  267. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_22_script_editor.svg +0 -0
  268. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_23_config_editor.svg +0 -0
  269. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_24_cmd_picker.svg +0 -0
  270. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_30_new_cfg.png +0 -0
  271. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_31_cli_completion.png +0 -0
  272. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_32_port_picker.png +0 -0
  273. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_33_port_tooltip.png +0 -0
  274. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_34_latency_timer.png +0 -0
  275. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_35_vt100_menu.png +0 -0
  276. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_36_vt100_status.png +0 -0
  277. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/img/doc_37_config_picker.svg +0 -0
  278. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/installation.md +0 -0
  279. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/on-ai-assistance.md +0 -0
  280. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/ports.md +0 -0
  281. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/help/vt100.md +0 -0
  282. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/.coverage +0 -0
  283. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/assets/images/favicon.png +0 -0
  284. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/assets/javascripts/LICENSE +0 -0
  285. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_01_main_tui.svg +0 -0
  286. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_02_help.svg +0 -0
  287. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_03_help_proto.svg +0 -0
  288. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_04_proto_send.svg +0 -0
  289. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_05_proto_crc.svg +0 -0
  290. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_06_proto_delay.svg +0 -0
  291. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_07_crc_python.svg +0 -0
  292. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_08_variables.svg +0 -0
  293. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_09_target_help.svg +0 -0
  294. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_10_config_info.svg +0 -0
  295. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_11_proto_run.svg +0 -0
  296. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_12_cap_struct.svg +0 -0
  297. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_13_cap_progress.svg +0 -0
  298. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_14_port_list.svg +0 -0
  299. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_15_xfer.svg +0 -0
  300. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_16_toolbar.svg +0 -0
  301. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_17_proto_debug.svg +0 -0
  302. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_20_palette.svg +0 -0
  303. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_21_script_picker.svg +0 -0
  304. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_22_script_editor.svg +0 -0
  305. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_23_config_editor.svg +0 -0
  306. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_24_cmd_picker.svg +0 -0
  307. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_30_new_cfg.png +0 -0
  308. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_31_cli_completion.png +0 -0
  309. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_32_port_picker.png +0 -0
  310. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_33_port_tooltip.png +0 -0
  311. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_34_latency_timer.png +0 -0
  312. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_35_vt100_menu.png +0 -0
  313. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_36_vt100_status.png +0 -0
  314. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/img/doc_37_config_picker.svg +0 -0
  315. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/html/sitemap.xml +0 -0
  316. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/info_views.py +0 -0
  317. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/install_info.py +0 -0
  318. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/mcp/__init__.py +0 -0
  319. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/mcp/prompts.py +0 -0
  320. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/palette_provider.py +0 -0
  321. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/plugins/handles/__init__.py +0 -0
  322. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/profile/__init__.py +0 -0
  323. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/profile/matcher.py +0 -0
  324. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/protocol/crc.py +0 -0
  325. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/protocol/viz.py +0 -0
  326. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/smoke.txt +0 -0
  327. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/templates/icons/termapy.icns +0 -0
  328. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/templates/icons/termapy.ico +0 -0
  329. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/templates/icons/termapy.png +0 -0
  330. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/templates/icons/termapy.svg +0 -0
  331. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/templates/linux/termapy.desktop.template +0 -0
  332. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/templates/macos/Termapy.app/Contents/Info.plist +0 -0
  333. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/templates/macos/Termapy.app/Contents/MacOS/launcher.template +0 -0
  334. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/update_check.py +0 -0
  335. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/usb/__init__.py +0 -0
  336. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/usb/_vendors_full.py +0 -0
  337. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/usb/aliases.py +0 -0
  338. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/usb/chips.py +0 -0
  339. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/usb/vendors.py +0 -0
  340. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/usb_tree.py +0 -0
  341. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/variables.py +0 -0
  342. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/LICENSES.md +0 -0
  343. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/__init__.py +0 -0
  344. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/__init__.py +0 -0
  345. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/__main__.py +0 -0
  346. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/rfc2217.py +0 -0
  347. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/rs485.py +0 -0
  348. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/serialcli.py +0 -0
  349. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/serialjava.py +0 -0
  350. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/serialposix.py +0 -0
  351. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/serialutil.py +0 -0
  352. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/serialwin32.py +0 -0
  353. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/threaded/__init__.py +0 -0
  354. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/tools/__init__.py +0 -0
  355. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/tools/hexlify_codec.py +0 -0
  356. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/tools/list_ports.py +0 -0
  357. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/tools/list_ports_common.py +0 -0
  358. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/tools/list_ports_linux.py +0 -0
  359. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/tools/list_ports_osx.py +0 -0
  360. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/tools/list_ports_posix.py +0 -0
  361. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/tools/list_ports_windows.py +0 -0
  362. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/tools/miniterm.py +0 -0
  363. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/urlhandler/__init__.py +0 -0
  364. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/urlhandler/protocol_alt.py +0 -0
  365. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/urlhandler/protocol_cp2110.py +0 -0
  366. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/urlhandler/protocol_hwgrep.py +0 -0
  367. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/urlhandler/protocol_loop.py +0 -0
  368. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/urlhandler/protocol_rfc2217.py +0 -0
  369. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/urlhandler/protocol_socket.py +0 -0
  370. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/urlhandler/protocol_spy.py +0 -0
  371. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/serial/win32.py +0 -0
  372. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/xmodem.py +0 -0
  373. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/ymodem/CRC.py +0 -0
  374. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/ymodem/Platform.py +0 -0
  375. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/ymodem/Protocol.py +0 -0
  376. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/ymodem/Socket.py +0 -0
  377. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/ymodem/Version.py +0 -0
  378. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vendor/ymodem/__init__.py +0 -0
  379. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/vt100.py +0 -0
  380. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/widgets/__init__.py +0 -0
  381. {termapy-0.76.0 → termapy-0.77.0}/src/termapy/widgets/checkbox.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: termapy
3
- Version: 0.76.0
3
+ Version: 0.77.0
4
4
  Summary: A TUI serial terminal with ANSI color support, built on Textual and pyserial
5
5
  Keywords: serial,terminal,tui,textual,pyserial,embedded,xmodem,ymodem
6
6
  Author: Chuck Bass
@@ -16,6 +16,7 @@ Classifier: Programming Language :: Python :: 3.11
16
16
  Classifier: Programming Language :: Python :: 3.12
17
17
  Classifier: Programming Language :: Python :: 3.13
18
18
  Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Programming Language :: Python :: 3.15
19
20
  Classifier: Topic :: Communications
20
21
  Classifier: Topic :: System :: Hardware
21
22
  Classifier: Topic :: Terminals :: Serial
@@ -50,7 +51,7 @@ Description-Content-Type: text/markdown
50
51
 
51
52
  **Powered by:** [![Textual](https://img.shields.io/badge/Textual-TUI-blue?logo=python)](https://textual.textualize.io/) [![pySerial](https://img.shields.io/badge/pySerial-serial%20I%2FO-orange?logo=python)](https://pyserial.readthedocs.io/) [![zensical](https://img.shields.io/badge/zensical-docs-green)](https://github.com/hucker/zensical)
52
53
 
53
- **Built with:** ![python](https://img.shields.io/badge/python-3.11--3.14-blue) [![uv](https://img.shields.io/badge/uv-package%20manager-blueviolet?logo=astral)](https://docs.astral.sh/uv/) [![pytest](https://img.shields.io/badge/pytest-testing-yellow?logo=pytest)](https://pytest.org/) [![coverage](https://img.shields.io/badge/coverage-72%25-yellow)](https://coverage.readthedocs.io/)
54
+ **Built with:** ![python](https://img.shields.io/badge/python-3.11--3.15-blue) [![uv](https://img.shields.io/badge/uv-package%20manager-blueviolet?logo=astral)](https://docs.astral.sh/uv/) [![pytest](https://img.shields.io/badge/pytest-testing-yellow?logo=pytest)](https://pytest.org/) [![coverage](https://img.shields.io/badge/coverage-84%25-brightgreen)](https://coverage.readthedocs.io/)
54
55
 
55
56
  Pronounced "ter-map-ee"
56
57
 
@@ -153,6 +154,8 @@ Termapy runs with your privileges and does whatever you — or a connected MCP c
153
154
  | Network sockets (`socket://`, `rfc2217://`) | `TERMAPY_MCP_NET_EGRESS=1` |
154
155
  | Environment variables (`/env`, `$(env.X)`) | `TERMAPY_MCP_ENV_ENABLED=1` |
155
156
 
157
+ Each flag takes any boolean token (`1`, `on`, `true`, `yes`, ...); a value outside that vocabulary refuses to start, naming the variable, rather than silently meaning off.
158
+
156
159
  <details>
157
160
  <summary><strong>The basics</strong> - keyboard shortcuts, title bar, REPL commands</summary>
158
161
 
@@ -194,15 +197,15 @@ The most common ones:
194
197
 
195
198
  | Command | Description |
196
199
  | ------------------------------------ | --------------------------------------------------- |
197
- | `/help [cmd]` | List commands or show help for one |
200
+ | `/help {cmd}` | List commands or show help for one |
198
201
  | `/port.list` | List available serial ports |
199
202
  | `/port.connect {name} {baud} {mode}` | Connect with optional baud rate and mode (e.g. N81) |
200
203
  | `/port.info` | Show port status and parameters |
201
- | `/cfg [key [value]]` | Open Cfg picker (bare TUI), or get/set values |
202
- | `/ss.svg [name]` | Save SVG screenshot |
204
+ | `/cfg {key {value}}` | Open Cfg picker (bare TUI), or get/set values |
205
+ | `/ss.svg {name}` | Save SVG screenshot |
203
206
  | `/cls` | Clear the terminal |
204
207
  | `/run {filename}` | Open Run picker (bare TUI), or run a named script |
205
- | `/term.echo [on \| off]` | Local echo of device commands sent to the wire |
208
+ | `/term.echo {on\|off\|toggle}` | Local echo of device commands sent to the wire |
206
209
  | `/grep <pattern>` | Search scrollback |
207
210
  | `/exit` | Exit termapy |
208
211
 
@@ -211,9 +214,9 @@ The most common ones:
211
214
 
212
215
  | Command | Description |
213
216
  | ------------------------------------ | ----------------------------------------------------------------------------- |
214
- | `/help [cmd]` | List commands or show extended help for one |
217
+ | `/help {cmd}` | List commands or show extended help for one |
215
218
  | `/help.dev <cmd>` | Show a command handler's Python docstring |
216
- | `/port {name}` | Open Port picker (bare TUI), list subcommands (bare CLI), or open by name |
219
+ | `/port {name}` | Open Port picker (bare TUI), list ports (bare CLI), or open by name |
217
220
  | `/port.help` | Same as `/help port` |
218
221
  | `/port.list` | List available serial ports |
219
222
  | `/port.connect {name} {baud} {mode}` | Connect with optional baud and mode (e.g. /port.connect COM3 9600 N81) |
@@ -225,21 +228,21 @@ The most common ones:
225
228
  | `/port.parity {value}` | Show or set parity (hardware only) |
226
229
  | `/port.stop_bits {value}` | Show or set stop bits (hardware only) |
227
230
  | `/port.flow_control {m}` | Show or set flow control: none, rtscts, xonxoff, manual |
228
- | `/port.dtr {0\|1}` | Show or set DTR line |
229
- | `/port.rts {0\|1}` | Show or set RTS line |
231
+ | `/port.dtr {on\|off}` | Show or set DTR line |
232
+ | `/port.rts {on\|off}` | Show or set RTS line |
230
233
  | `/port.cts` | Show CTS state (read-only) |
231
234
  | `/port.dsr` | Show DSR state (read-only) |
232
235
  | `/port.ri` | Show RI state (read-only) |
233
236
  | `/port.cd` | Show CD state (read-only) |
234
237
  | `/port.break {ms}` | Send break signal (default 250ms) |
235
- | `/cfg [key [value]]` | Open Cfg picker (bare TUI), dump JSON (bare CLI), or get/set with args |
238
+ | `/cfg {key {value}}` | Open Cfg picker (bare TUI), list configs (bare CLI), or get/set with args |
236
239
  | `/cfg.auto <key> <value>` | Set an in-memory config key immediately (no confirmation) |
237
240
  | `/cfg.list` | List all config files |
238
241
  | `/cfg.load <name>` | Switch to a different config by name |
239
242
  | `/cfg.show` | Open the current config file in the system viewer |
240
243
  | `/cfg.help` | Same as `/help cfg` |
241
- | `/ss.svg [name]` | Save SVG screenshot |
242
- | `/ss.txt [name]` | Save text screenshot |
244
+ | `/ss.svg {name}` | Save SVG screenshot |
245
+ | `/ss.txt {name} {N}` | Save text screenshot (all, or an N-line slice) |
243
246
  | `/ss.list` | List screenshots, newest first, with size and age |
244
247
  | `/ss.explore` | Open the screenshot folder in the system file explorer |
245
248
  | `/cls` | Clear the terminal screen |
@@ -258,34 +261,51 @@ The most common ones:
258
261
  | `/show.cfg` | Show the current config file |
259
262
  | `/term` | Terminal display / session toggles (echo, line_no, timestamps, ...) |
260
263
  | `/term.info` | Snapshot the state of every `/term.*` toggle |
261
- | `/term.echo [on \| off]` | Local echo of device commands sent to the wire (persisted via cfg `echo`) |
262
- | `/term.echo_repl [on \| off]` | Local echo of REPL (slash) commands, session only; off inside scripts |
263
- | `/term.line_no [on \| off]` | Toggle line numbers in serial output (TUI only) |
264
- | `/term.eol.markers [on \| off]` | Toggle visible `\r` `\n` markers for line-ending troubleshooting |
265
- | `/term.output {level}` | Show or set output level (silent/quiet/normal/verbose) |
266
- | `/term.timestamps [on \| off]` | Toggle `[HH:MM:SS.mmm]` timestamp prefix |
267
- | `/term.hex [on \| off]` | Toggle hex display of incoming bytes |
264
+ | `/term.echo {on\|off\|toggle}` | Local echo of device commands sent to the wire (persisted via cfg `echo`) |
265
+ | `/term.echo_repl {on\|off\|toggle}` | Local echo of REPL (slash) commands, session only; off inside scripts |
266
+ | `/term.line_no {on\|off\|toggle}` | Toggle line numbers in serial output (TUI only) |
267
+ | `/term.eol.markers {on\|off\|toggle}` | Toggle visible `\r` `\n` markers for line-ending troubleshooting |
268
+ | `/term.output {level\|cycle}` | Show or set output level (silent/quiet/normal/verbose) |
269
+ | `/term.timestamps {on\|off\|toggle}` | Toggle `[HH:MM:SS.mmm]` timestamp prefix |
270
+ | `/term.hex {on\|off\|toggle}` | Toggle hex display of incoming bytes |
268
271
  | `/term.encoding {name}` | Show or set byte-decoding encoding (utf-8, latin-1, ...) |
269
- | `/term.send_bare_enter [on \| off]` | Send line ending on empty Enter |
272
+ | `/term.json {compact\|indent\|color\|cycle}` | How JSON answers print: one line, indented, or indented and colored |
273
+ | `/term.send_bare_enter {on\|off\|toggle}` | Send line ending on empty Enter |
270
274
  | `/edit <file>` | Edit a project file (`run/`/`proto/` path) |
271
275
  | `/edit.cfg` | Edit the current config file |
272
276
  | `/edit.log` | Open the session log in the system viewer |
273
277
  | `/edit.info` | Open the info report in the system viewer |
274
278
  | `/os <cmd>` | Run a shell command (10s timeout, requires `TERMAPY_OS_CMD_ENABLED=1` in env) |
275
279
  | `/grep <pattern>` | Search scrollback for regex matches (case-insensitive, skips own output) |
276
- | `/cfg.info {--display}` | Show project summary (folder tree + config + buttons); `--display` opens the full report |
277
- | `/proto` | Open Proto picker (bare TUI) or show long-help (bare CLI) |
280
+ | `/cfg.info {--display}` | Write and show the project report (folder tree, config, buttons, log and history tails); also written at exit; `--display` opens it |
281
+ | `/proto` | Open Proto picker (bare TUI) or list proto/ (bare CLI) |
278
282
  | `/proto.help` | Same as `/help proto` |
279
283
  | `/proto.send <hex>` | Send raw hex bytes and/or quoted text, display response as hex (see below) |
280
284
  | `/proto.run <file>` | Run a binary protocol test script (.pro) with pass/fail |
281
285
  | `/proto.list` | List .pro files in proto/, newest first, with size and age |
282
286
  | `/proto.rename <old> <new>` | Rename a protocol script (`.pro` kept; never overwrites) |
283
- | `/proto.hex [on \| off]` | Toggle hex display mode for serial I/O |
287
+ | `/proto.hex {on\|off\|toggle}` | Same flag as `/term.hex` |
284
288
  | `/proto.crc.list {pat}` | List available CRC algorithms (optional glob filter) |
285
289
  | `/proto.crc.info <name>` | Show CRC algorithm parameters and description |
286
- | `/proto.crc.calc <n> {d}` | Compute CRC over hex bytes, text, or file; omit data to verify check string |
287
- | `/proto.crc.find <pkt>` | Identify CRC algorithm from a captured packet (bin= hex or asc= text) |
290
+ | `/proto.crc.calc <n> {d}` | Compute CRC over hex bytes, quoted text, or a file; omit data to verify check string |
291
+ | `/proto.crc.find bin=<hex>\|asc=<text>\|cmd=<trigger>` | Identify CRC algorithm from a captured packet, text, or a command's reply |
288
292
  | `/proto.info` | Print current protocol state |
293
+ | `/mem.dump <target> {len} {type}` | Hexdump or typed word columns at an address or symbol |
294
+ | `/mem.read <target> {type}` | One typed value: scalar, char, register field, bit or slice |
295
+ | `/mem.write <target> <hex>` | Write hex bytes, or set a register field/bit (audited; destructive over MCP) |
296
+ | `/mem.or <target> <mask>` | Boolean ops on one word (also `.and`, `.clear`, `.xor`, `.not`); atomic via `MEM.M` where expressible |
297
+ | `/mem.str <target> {max}` | Read a NUL-terminated string as a first-class value |
298
+ | `/mem.info` | Memory access config: dialect, block limit, width, endian, atomic modify |
299
+ | `/sym <addr\|name>` | Symbol table: name to address, or address to `name+offset` |
300
+ | `/sym.import {file} {format=<value>}` | Convert a linker map to `sym/<cfg>.symbols.json` and load it (bare: re-import the same map) |
301
+ | `/sym.info` | Show the loaded symbol table: file, source, counts by section, range |
302
+ | `/sym.load {path}` | Load a symbol table (default: the cfg sidecar `/sym.import` writes) |
303
+ | `/sym.search <pattern>` | Search symbols by name: exact, glob, regex, or substring |
304
+ | `/sym.unload` | Clear the loaded symbol table |
305
+ | `/dev.import <file> {format=<value>} {category=<value>} {to=<value>} {use=on\|off}` | Convert a vendor register description (CMSIS-SVD) into this config's `lib/` and reference it from `dev/` (`to=global` shares it) |
306
+ | `/dev.lib {pattern}` | List the parts the libraries hold (available, not loaded), with their layer |
307
+ | `/dev.list` | List the devices this config has loaded, with layer and placement |
308
+ | `/dev.use <part> {at=<value>}` | Add a library part to this config; `at=NAME@ADDR ...` places a relocatable one |
289
309
  | `/var {name}` | List user variables, or show one by name |
290
310
  | `/var.set <NAME> <value>` | Set a user variable |
291
311
  | `/var.clear` | Clear all user variables |
@@ -297,7 +317,7 @@ The most common ones:
297
317
  | `/cap.struct <f> ...` | Capture binary data, decode with format spec to CSV |
298
318
  | `/cap.hex <f> ...` | Capture hex text lines, decode with format spec to CSV |
299
319
  | `/cap.stop` | Stop an active capture |
300
- | `/raw <text>` | Send text to serial with no variable expansion or transforms |
320
+ | `/raw <text>` | Send text exactly as typed: no line ending, expansion or transforms |
301
321
  | `/exit` | Exit termapy |
302
322
 
303
323
  </details>
@@ -315,12 +335,13 @@ Everything termapy creates (configs, scripts, test files, plugins, logs) lives i
315
335
 
316
336
  ```text
317
337
  termapy_cfg/
318
- ├── plugin/ # global plugins (all configs)
338
+ ├── plugin/ # global plugins (all configs), once you add one
319
339
  └── demo/
320
340
  ├── demo.cfg # config file
341
+ ├── demo_legacy.profile.json # /mem.* through the device's own `mem` grammar
321
342
  ├── demo.log # session log
322
343
  ├── demo.history # command history
323
- ├── ss/ # screenshots
344
+ ├── demo.md # project report (/cfg.info), rewritten at exit
324
345
  ├── run/ # script files for /run
325
346
  │ ├── at_demo.run
326
347
  │ ├── smoke_test.run
@@ -331,16 +352,14 @@ termapy_cfg/
331
352
  │ ├── probe.py
332
353
  │ ├── temp_plot.py
333
354
  │ └── traffic.py
334
- ├── cap/ # data capture output files
335
- ├── prof/ # /run.profile timing CSVs
336
- ├── viz/ # per-config packet visualizers
355
+ ├── sym/ # symbol tables (demo.symbols.json, auto-loaded)
337
356
  └── proto/ # protocol test scripts
338
357
  ├── at_test.pro
339
358
  ├── bitfield_inline.pro
340
359
  └── modbus_inline.pro
341
360
  ```
342
361
 
343
- Your own configs follow the same layout. Create one with `Cfg` → `New` and termapy builds the folder structure automatically.
362
+ Your own configs follow the same layout, minus the demo content: a new config is its `.cfg` beside the log and history. Data folders appear on first use (`ss/` at the first screenshot, `cap/` at the first capture, `prof/` at the first `/run.profile`, `viz/` when you drop a visualizer in), and a folder you empty is removed at the next load, so a plain terminal session never sees a folder it has no use for.
344
363
 
345
364
  ### Version control
346
365
 
@@ -356,19 +375,18 @@ Since COM port names differ between machines, use `$(env.NAME)` placeholders in
356
375
 
357
376
  ```json
358
377
  {
359
- "port": "$(env.COMPORT|COM4)",
360
- "baud_rate": 115200,
378
+ "serial": {"port": "$(env.COMPORT|COM4)", "baud_rate": 115200},
361
379
  "auto_connect": true
362
380
  }
363
381
  ```
364
382
 
365
- On a machine with `COMPORT=COM7`, termapy connects to COM7. On a machine without `COMPORT` set, it falls back to COM4. The config file on disk keeps the raw `$(env.COMPORT|COM4)` template. It's expanded in memory at load time, so your checked-in config stays portable.
383
+ On a machine with `COMPORT=COM7`, termapy connects to COM7. On a machine without `COMPORT` set, it falls back to COM4. The config file on disk keeps the raw `$(env.COMPORT|COM4)` template. It's expanded in memory at load time, so your checked-in config stays portable. A boolean key can be templated the same way (`"echo": "$(env.ECHO|false)"`); the expanded text is read with the boolean vocabulary (`on`, `off`, `true`, `false`, `yes`, `no`, `1`, `0`, ...), and a value outside it refuses the load naming the key.
366
384
 
367
- Environment variables work in any string config value, not just `port`:
385
+ Environment variables work in any string config value, not just `serial.port`:
368
386
 
369
387
  ```json
370
388
  {
371
- "port": "$(env.COMPORT|COM4)",
389
+ "serial": {"port": "$(env.COMPORT|COM4)"},
372
390
  "title": "$(env.DEVICE_NAME|Dev Board)",
373
391
  "log_file": "$(env.LOG_DIR|logs)/session.log"
374
392
  }
@@ -449,7 +467,7 @@ Any datetime variable takes an optional `strftime` format after a colon — `$(D
449
467
 
450
468
  **vs. environment variables:** `$(env.NAME)` pulls from the OS environment and works in config files. `$(NAME)` is for user-defined session variables in commands and scripts. Both use the `$(...)` syntax. The `env.` prefix is required to access environment variables explicitly.
451
469
 
452
- **Escaping:** Use `\$` to prevent expansion of a single reference, or `/raw` to skip expansion for an entire line.
470
+ **Escaping:** Use `\$` to prevent expansion of a single reference, or `/term.send` to send an entire line without expansion (`/raw` does the same with no line ending).
453
471
 
454
472
  Add a `.gitignore` for session files you don't need to track:
455
473
 
@@ -500,13 +518,12 @@ When you create a new config, termapy writes a complete `.cfg` file with all def
500
518
 
501
519
  ```json
502
520
  {
503
- "port": "COM4",
504
- "baud_rate": 115200,
521
+ "serial": {"port": "COM4", "baud_rate": 115200},
505
522
  "auto_connect": true,
506
523
  "auto_reconnect": true,
507
524
  "title": "Sensor A",
508
525
  "border_color": "blue",
509
- "on_connect_cmd": "rev \n help dev"
526
+ "on_connect_cmd": "/run startup"
510
527
  }
511
528
  ```
512
529
 
@@ -534,13 +551,11 @@ Add toolbar buttons that send commands, run scripts, or chain multiple actions.
534
551
 
535
552
  ### Hardware line control
536
553
 
537
- Set `flow_control` to `"manual"` to get DTR, RTS, and Break buttons in the toolbar. This is useful for devices that use these lines for reset or bootloader entry:
554
+ Set `serial.flow_control` to `"manual"` to get DTR, RTS, and Break buttons in the toolbar. This is useful for devices that use these lines for reset or bootloader entry:
538
555
 
539
556
  ```json
540
557
  {
541
- "port": "COM4",
542
- "baud_rate": 115200,
543
- "flow_control": "manual",
558
+ "serial": {"port": "COM4", "baud_rate": 115200, "flow_control": "manual"},
544
559
  "title": "Hardware Debug"
545
560
  }
546
561
  ```
@@ -551,7 +566,7 @@ Set `flow_control` to `"manual"` to get DTR, RTS, and Break buttons in the toolb
551
566
  <!-- validate-config-keys -->
552
567
  ```json
553
568
  {
554
- "config_version": 29,
569
+ "config_version": 30,
555
570
  "title": "",
556
571
  "border_color": "",
557
572
  "max_lines": 10000,
@@ -602,6 +617,7 @@ Set `flow_control` to `"manual"` to get DTR, RTS, and Break buttons in the toolb
602
617
  "hex": false,
603
618
  "request_mode": false,
604
619
  "request_err_pattern": "(?i)^(ERROR|ERR|FAULT)\\b",
620
+ "json_style": "compact",
605
621
  "strip_device_echo": false,
606
622
  "max_grep_lines": 100,
607
623
  "file_xfer_root": "",
@@ -761,18 +777,22 @@ expect_fmt = "Title:Modbus_Response Slave:H1 Func:H2 Bytes:U3 R0:U4-5 R1:U6-7 CR
761
777
 
762
778
  Each column is `Name:TypeBytes` where the type controls how bytes are displayed:
763
779
 
764
- | Type | Description | Example | Display |
765
- | ------ | ------------------------- | --------------------- | ------------------ |
766
- | `H` | Hex (uppercase) | `H1` / `H3-4` | `0A` / `0A 2B` |
767
- | `h` | Hex (lowercase) | `h1` | `0a` |
768
- | `U` | Unsigned int (big-endian) | `U3-4` | `7` |
769
- | `I` | Signed int (big-endian) | `I3-4` | `-1` |
770
- | `S` | ASCII string | `S3-10` | `HELLO` |
771
- | `B` | Bit field (integer) | `B4-5.0-2` | `3` |
772
- | `b` | Bit field (binary string) | `b4-5.0-15` | `0000101000101011` |
773
- | `F` | IEEE 754 float | `F3-6` | `3.14` |
774
- | `_` | Padding (skip bytes) | `_3-4` | *(hidden)* |
775
- | `crc*` | CRC auto-check | `CRC:crc16-modbus_le` | `OK` / `FAIL` |
780
+ <!-- format-types:start (generated by scripts/sync_format_tables.py from protocol/type_codes.py -- do not edit by hand) -->
781
+
782
+ | Code | Meaning | Example | Output |
783
+ | ------ | ---------------- | ------------------ | ------------ |
784
+ | `H` | Hex bytes | `H1`, `H3-4` | `0A`, `01FF` |
785
+ | `h` | Hex, per byte | `h3-4` | `01 FF` |
786
+ | `U` | Unsigned integer | `U1`, `U3-4` | `10`, `256` |
787
+ | `I` | Signed integer | `I1`, `I3-4` | `-1`, `+127` |
788
+ | `S` | ASCII string | `S1-5` | `Hello` |
789
+ | `F` | IEEE 754 float | `F1-4` | `3.14` |
790
+ | `B` | Bit field | `B1.3`, `B1-2.7-9` | `1`, `5` |
791
+ | `b` | Bit field, bits | `b1.0-7` | `00000110` |
792
+ | `_` | Padding (hidden) | `_3-4` | *(skipped)* |
793
+ | `crc*` | CRC verify | `crc16-modbus` | pass/fail |
794
+
795
+ <!-- format-types:end -->
776
796
 
777
797
  Byte indices are 1-based. Ranges use `-` (e.g. `U3-4` = bytes 3-4). **Byte order is controlled by the index direction.** This is how you handle big-endian vs little-endian protocols:
778
798
 
@@ -942,7 +962,7 @@ Passing a `.run` file to `--cli` automatically infers the config from the file's
942
962
 
943
963
  - Rich colored output (toggle with `/term.color on|off` or `--no-color`)
944
964
  - Command history shared with TUI (up/down arrows, persisted across sessions)
945
- - Tab completion for REPL commands
965
+ - Tab completion for REPL commands and for the file names they take (`/run`, `/proto.run`, `/cap.dump`, ...)
946
966
  - Script execution with `/run` (same scripts work in both TUI and CLI)
947
967
  - `/delay` with progress bar for waits of a second or longer (Ctrl+C to cancel)
948
968
  - All `/port`, `/cfg`, `/var`, `/env`, `/proto.crc`, `/edit` commands work
@@ -983,6 +1003,10 @@ It's also a reversible toggle from inside the TUI, like `/cli` <-> `/tui`: `/vt1
983
1003
  <details>
984
1004
  <summary><strong>Extending termapy</strong> - plugins, subcommands, visualizers</summary>
985
1005
 
1006
+ ### Symbols, memory and parts
1007
+
1008
+ `/sym.import build/mem.map` turns your linker map into names, so `/mem.read gTemp` reads a variable by name instead of by address. `/dev.import part.svd` does the same for the silicon: a vendor CMSIS-SVD becomes a **device file** -- every register of the part with its bit fields -- stored once in the config's `lib/` and referenced from its `dev/`, so `/mem.read PORT_GROUP0_DIR` works and a checked-in config folder carries its parts. A part on the external bus (two ADCs, an FPGA block) is written once with register offsets and placed per board: `/dev.use acme-adc16 at=ADC1@0x60000000 ADC2@0x60001000`. Registers are live silicon, not variables, so `/mem.*` refuses bulk reads over them, refuses reads of write-only registers, and logs the rest. The device side is the MEM wire spec (`examples/firmware/termapy_mem.c`, a drop-in) or a profile block describing your monitor's own peek/poke grammar. See the Symbols, Devices and Memory help pages.
1009
+
986
1010
  ### Plugins
987
1011
 
988
1012
  Every built-in command (`/help`, `/cfg`, `/grep`, all of them) is itself a plugin loaded from the same folder you'd drop your own into. If something was hard to build as a plugin, the API was wrong. [Dogfooding](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) all the way down.
@@ -1013,86 +1037,9 @@ COMMAND = Command(
1013
1037
  3. **Per-config:** `termapy_cfg/<name>/plugin/*.py`, specific to one config
1014
1038
  4. **App hooks:** frontend-specific commands (`/ss`, `/delay`, `/run`, etc.)
1015
1039
 
1016
- <details>
1017
- <summary>Subcommands</summary>
1018
-
1019
- Use `sub_commands` for related operations. Users invoke them with dot notation (`/tool.run`):
1020
-
1021
- ```python
1022
- from termapy.plugins import Command
1023
-
1024
- def _run(ctx, args):
1025
- ctx.io.status(f"Running {args}...")
1040
+ Resolution is the same in every frontend (TUI, CLI, MCP server) and reruns on every config switch; a folder plugin can never take an app hook's name.
1026
1041
 
1027
- def _status(ctx, args):
1028
- ctx.io.result("All good.")
1029
-
1030
- # ── COMMAND (must be at end of file) ──────────────────────────────────────────
1031
- COMMAND = Command(
1032
- name="tool",
1033
- help="A tool with subcommands.",
1034
- sub_commands={
1035
- "run": Command(args="<file>", help="Run a file.", handler=_run),
1036
- "status": Command(help="Show status.", handler=_status),
1037
- },
1038
- )
1039
- ```
1040
-
1041
- The user types `/tool.run myfile` or `/tool.status`.
1042
-
1043
- </details>
1044
-
1045
- <details>
1046
- <summary>PluginContext API</summary>
1047
-
1048
- The `ctx` object passed to every handler is a thin shell over **five capability handles**, each owning one domain:
1049
-
1050
- | Member | Description |
1051
- | --------------------------------- | ----------------------------------------------------------------- |
1052
- | `ctx.cfg` | Current config (read-only mapping) |
1053
- | `ctx.config_path` | Path to the current `.cfg` config file |
1054
- | `ctx.io.result(text, color)` | The command's answer; shown at quiet+ (suppressed at silent) |
1055
- | `ctx.io.output(text, color)` | Bulk data: listings, dumps; shown at normal+ |
1056
- | `ctx.io.status(text)` | Progress chatter; shown only at verbose |
1057
- | `ctx.io.result_markup(text)` | Like `result` but text is Rich markup (`[red]X[/]`) |
1058
- | `ctx.io.output_markup(text)` | Like `output` but text is Rich markup |
1059
- | `ctx.io.status_markup(text)` | Like `status` but text is Rich markup |
1060
- | `ctx.io.notify(text)` | Always-works fallback notification (toast in TUI, plain in CLI) |
1061
- | `ctx.io.log(prefix, text)` | Write to session log: `">"` TX, `"<"` RX, `"#"` status |
1062
- | `ctx.serial.is_connected` | Bool: serial port is open |
1063
- | `ctx.serial.write(data)` | Send bytes to the serial port (auto-logged as TX) |
1064
- | `ctx.serial.read_raw(timeout_ms)` | Read raw bytes with timeout framing (returns `bytes`) |
1065
- | `ctx.serial.drain()` | Drain pending RX data |
1066
- | `ctx.serial.wait_idle()` | Wait until serial output settles |
1067
- | `ctx.serial.io()` | Context manager for exclusive serial I/O |
1068
- | `ctx.serial.rx_observer()` | Context manager: passive RX byte tap |
1069
- | `ctx.serial.tx_observer()` | Context manager: passive TX byte tap |
1070
- | `ctx.fs.ss_dir` | Screenshot directory (`Path`) |
1071
- | `ctx.fs.scripts_dir` | Scripts directory (`Path`) |
1072
- | `ctx.fs.proto_dir` | Proto-test directory (`Path`) |
1073
- | `ctx.fs.cap_dir` | Capture-output directory (`Path`) |
1074
- | `ctx.fs.open_file(path)` | Open in system viewer/editor (gated on `gui_apps` capability) |
1075
- | `ctx.ui.confirm(message)` | Yes/Cancel dialog (TUI-only; declare `confirm_dialog` capability) |
1076
- | `ctx.ui.notify(text)` | TUI-strict notification (declare `ui_notify`) |
1077
- | `ctx.ui.clear_screen()` | Clear the terminal output (declare `tui_mode`) |
1078
- | `ctx.ui.screenshot(path)` | Save an SVG screenshot (declare `screen_capture`) |
1079
- | `ctx.dispatch(cmd)` | Re-route a command through the full pipeline |
1080
- | `ctx.ns(name)` | Get/create a session-scoped state dict |
1081
- | `ctx.plugin_cfg(name)` | Get a per-plugin persistent config dict |
1082
-
1083
- **Capability gating.** Methods on `ctx.ui` and `ctx.fs.open_file` are gated on `CapabilitySet` flags. Calling a gated method without declaring the capability raises `MissingCapability`, which the dispatcher converts to `CmdResult.fail`. Declare what your command needs:
1084
-
1085
- ```python
1086
- COMMAND = Command(
1087
- name="ask", help="Prompt user.",
1088
- needs=CapabilitySet(confirm_dialog=True), # refuse to dispatch in CLI
1089
- handler=_handler,
1090
- )
1091
- ```
1092
-
1093
- There is also `ctx.internal`, a privileged escape hatch for built-ins (config save, capture, port control, engine forwarders, etc.). This is used by built-in commands and may change between versions, so external plugins should avoid it.
1094
-
1095
- </details>
1042
+ Subcommands (`/tool.run`, `/tool.status`), typed parameters, scriptable return values, the full `PluginContext` API with its five handles and capability gating, and the `ctx.internal` escape hatch that built-ins use are all in the [Writing plugins](src/termapy/help/writing-plugins.md) guide, which also ships in the in-app help.
1096
1043
 
1097
1044
  <details>
1098
1045
  <summary>Example plugins</summary>
@@ -1104,7 +1051,6 @@ See `examples/plugins/` for working examples:
1104
1051
  - **timestamp.py:** print the current date/time
1105
1052
  - **ping.py:** send a command and measure response time
1106
1053
  - **traffic.py:** passive RX/TX byte tap (count, hexdump, rate, snoop) -- also shipped with `--demo`
1107
- - **pic_map.py:** decode a PIC memory map
1108
1054
 
1109
1055
  More complete examples ship with `--demo`: `probe.py` demonstrates the drain → write → read → parse cycle for device interaction; `traffic.py` (with `/traffic.count`, `/traffic.hexdump`, `/traffic.rate`, `/traffic.snoop`) demonstrates the passive RX/TX observer pattern via the `ctx.serial.rx_observer()` / `ctx.serial.tx_observer()` context managers. Run `/help probe` or `/help traffic` to see the documentation, or `/help.dev <name>` for the source docstrings.
1110
1056
 
@@ -1140,16 +1086,22 @@ Actual: 01 00 C9 FF FE 0A -> ID:01 Temp:201 Signed:-2 Status:0A
1140
1086
  <details>
1141
1087
  <summary>Supported types</summary>
1142
1088
 
1089
+ <!-- format-types:start (generated by scripts/sync_format_tables.py from protocol/type_codes.py -- do not edit by hand) -->
1090
+
1143
1091
  | Code | Meaning | Example | Output |
1144
1092
  | ------ | ---------------- | ------------------ | ------------ |
1145
1093
  | `H` | Hex bytes | `H1`, `H3-4` | `0A`, `01FF` |
1094
+ | `h` | Hex, per byte | `h3-4` | `01 FF` |
1146
1095
  | `U` | Unsigned integer | `U1`, `U3-4` | `10`, `256` |
1147
1096
  | `I` | Signed integer | `I1`, `I3-4` | `-1`, `+127` |
1148
- | `S` | ASCII string | `S5-12` | `Hello...` |
1097
+ | `S` | ASCII string | `S1-5` | `Hello` |
1149
1098
  | `F` | IEEE 754 float | `F1-4` | `3.14` |
1150
1099
  | `B` | Bit field | `B1.3`, `B1-2.7-9` | `1`, `5` |
1151
- | `_` | Padding (hidden) | `_:_3-4` | *(skipped)* |
1152
- | `crc*` | CRC verify | `CRC:crc16-modbus_le` | pass/fail |
1100
+ | `b` | Bit field, bits | `b1.0-7` | `00000110` |
1101
+ | `_` | Padding (hidden) | `_3-4` | *(skipped)* |
1102
+ | `crc*` | CRC verify | `crc16-modbus` | pass/fail |
1103
+
1104
+ <!-- format-types:end -->
1153
1105
 
1154
1106
  Integers support 1, 2, 3, 4, and 8 byte widths. Floats are 4-byte (F32) or 8-byte (F64).
1155
1107
 
@@ -1239,30 +1191,19 @@ From the REPL:
1239
1191
  <details>
1240
1192
  <summary><strong>Portability</strong></summary>
1241
1193
 
1242
- Developed and tested on **Windows**. Basic usage verified on **macOS** (serial, ANSI rendering, screenshots). macOS support is **alpha** until further testing. **Linux** is exercised by GitHub Actions CI on every push (the full test suite, including the CLI gold-standard integration test, runs on Ubuntu against Python 3.11 through 3.14), but interactive use on Linux has not been hand-verified by the author.
1194
+ Developed and tested on **Windows**. Basic usage verified on **macOS** (serial, ANSI rendering, screenshots). macOS support is **alpha** until further testing. **Linux** is exercised by GitHub Actions CI on every push (the full test suite, including the CLI gold-standard integration test, runs on Ubuntu against Python 3.11 through 3.15), but interactive use on Linux has not been hand-verified by the author.
1243
1195
 
1244
1196
  </details>
1245
1197
 
1246
1198
  <details>
1247
1199
  <summary><strong>Architecture</strong> - threading model</summary>
1248
1200
 
1249
- Textual runs on a single async event loop. Termapy uses `@work(thread=True)` for blocking operations, posting UI updates via `call_from_thread()`.
1250
-
1251
- | Worker | Lifetime | Purpose |
1252
- | ------------------- | ----------- | ------------------------------------------------------- |
1253
- | `SerialEngine.read_loop()` | Long-lived | The reader thread (`start_reader` / `stop_reader`); hands RX to the UI without blocking |
1254
- | `_run_reconnect()` | Short-lived | Retries serial connection every 2.5s until success |
1255
- | `_run_lines()` | Short-lived | Sends multiple commands with inter-command delay |
1256
- | `_run_script()` | Short-lived | Executes a `.run` script file line by line |
1257
- | `_send_test()` | Short-lived | Runs a single protocol test case (`proto_debug.py`) |
1258
- | `_run_cmds()` | Short-lived | Sends setup/teardown commands for protocol tests (`proto_debug.py`) |
1259
-
1260
- Only the reader is long-lived. At most two workers run concurrently: the serial reader plus one command/script/test worker.
1201
+ Every command runs on a worker thread, the serial reader is the only long-lived thread, and the UI thread never parks on a lock a worker holds. The one description of the threads, their hand-offs, and the rules each relies on is the [Threading model](ARCHITECTURE.md#threading-model) section of ARCHITECTURE.md.
1261
1202
 
1262
1203
  </details>
1263
1204
 
1264
1205
  <details>
1265
- <summary><strong>Test coverage</strong> - 3394 tests, 72% core-module coverage</summary>
1206
+ <summary><strong>Test coverage</strong> - 4913 tests, 84% core-module coverage</summary>
1266
1207
 
1267
1208
  Run the full suite with `uv run pytest`. (The count above is the one place it
1268
1209
  is tracked; `release_prep` refreshes it each release.)
@@ -1279,7 +1220,7 @@ generator permutation.
1279
1220
 
1280
1221
  **Built-in plugins:** broad coverage via `test_builtins.py` plus per-plugin test files (`test_var.py`, `test_env_var.py`, `test_xmodem.py`, `test_ymodem.py`, `test_app_plugin.py`, `test_proto_send_crc.py`, etc.).
1281
1222
 
1282
- **UI code:** `app.py` is driven headless by `tests/test_app_pilot.py` (Textual Pilot boots the real app), and the dialogs have Pilot tests of their own; `proto_debug.py` is still exercised manually. The headline coverage figure is **core-module** coverage, measured with `dialogs/`, the `builtins/` package top level, and vendored code **omitted** (see `[tool.coverage.run]` in `pyproject.toml`) — so it is *not* whole-repo coverage. Counting the whole repo (only vendored third-party code omitted) is lower, and that gap is exactly this untested UI layer. The omit is deliberate — the focus has been on extracting business logic into unit-testable modules and keeping the UI as thin delegation — but the headline number is core-module, not overall.
1223
+ **UI code:** `app.py` is driven headless by `tests/test_app_pilot*.py` (Textual Pilot boots the real app), every dialog by `tests/test_dialogs*.py` (each pushed into a bare host app), and `proto_debug.py` has Pilot coverage for its Run and Stop controls and is otherwise exercised manually. The headline coverage figure is measured with the `builtins/` package top level and vendored code **omitted** (see `[tool.coverage.run]` in `pyproject.toml`): `builtins/` are plugins covered behaviorally through the commands they register, `vendor/` is third-party. `app.py` and `dialogs/` are counted, so the UI layer's number is real and movable rather than a flattering omission.
1283
1224
 
1284
1225
  </details>
1285
1226
 
@@ -1290,9 +1231,9 @@ All tests run automatically on push to `main` and on pull requests via GitHub Ac
1290
1231
 
1291
1232
  | Job | What it does |
1292
1233
  | ------------ | ------------------------------------------------------------------------------------------------- |
1293
- | **test** | Runs `pytest` across Python 3.11, 3.12, 3.13, and 3.14 |
1234
+ | **test** | Runs `pytest` across Python 3.11, 3.12, 3.13, 3.14, and 3.15 |
1294
1235
  | **coverage** | Runs `pytest --cov` on Python 3.14 and uploads to [Codecov](https://codecov.io/gh/hucker/termapy) |
1295
- | **audit** | Runs `pip-audit` to check for known vulnerabilities in dependencies |
1236
+ | **audit** | Audits `uv.lock` with `pip-audit` (every extra and group; `scripts/audit_lock.py`) |
1296
1237
 
1297
1238
  The CI badge at the top of this README reflects the current status of the test workflow. See [`.github/workflows/tests.yml`](.github/workflows/tests.yml) for the full configuration.
1298
1239