dexterm-cli 0.1.6 → 0.2.0

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 (395) hide show
  1. package/dist/packages/agents/src/schema.js +22 -1
  2. package/dist/packages/agents/src/spectator.js +3 -1
  3. package/dist/packages/agents/src/story.js +11 -2
  4. package/dist/packages/agents/src/tiers.js +36 -2
  5. package/dist/packages/agents/src/tools.js +35 -2
  6. package/dist/packages/autotrade/assets/exit-profile.schema.json +492 -0
  7. package/dist/packages/autotrade/assets/limits.schema.json +569 -0
  8. package/dist/packages/autotrade/assets/profiles/moonbag.json +46 -0
  9. package/dist/packages/autotrade/assets/profiles/runner.json +88 -0
  10. package/dist/packages/autotrade/assets/profiles/scalp.json +88 -0
  11. package/dist/packages/autotrade/assets/profiles/swing.json +88 -0
  12. package/dist/packages/autotrade/assets/strategies/moonbag.json +101 -0
  13. package/dist/packages/autotrade/assets/strategies/runner.json +101 -0
  14. package/dist/packages/autotrade/assets/strategies/sample-dip-ladder.json +188 -0
  15. package/dist/packages/autotrade/assets/strategies/scalp.json +98 -0
  16. package/dist/packages/autotrade/assets/strategies/swing.json +97 -0
  17. package/dist/packages/autotrade/assets/strategies/yolo.json +157 -0
  18. package/dist/packages/autotrade/assets/tapes/yolo-rehearsal.json +1 -0
  19. package/dist/packages/autotrade/examples/executor/README.md +70 -0
  20. package/dist/packages/autotrade/examples/executor/jsonl-tail-executor.mjs +57 -0
  21. package/dist/packages/autotrade/examples/executor/kit.mjs +144 -0
  22. package/dist/packages/autotrade/examples/executor/signal-cmd-executor.mjs +25 -0
  23. package/dist/packages/autotrade/examples/executor/telegram-bot-recipe.md +47 -0
  24. package/dist/packages/autotrade/examples/executor/webhook-executor.mjs +47 -0
  25. package/dist/packages/autotrade/src/account.js +74 -0
  26. package/dist/packages/autotrade/src/backtest.js +358 -0
  27. package/dist/packages/autotrade/src/basis-text.js +32 -0
  28. package/dist/packages/autotrade/src/chat/approvals.js +484 -0
  29. package/dist/packages/autotrade/src/chat/audit.js +140 -0
  30. package/dist/packages/autotrade/src/chat/config.js +95 -0
  31. package/dist/packages/autotrade/src/chat/connector.js +24 -0
  32. package/dist/packages/autotrade/src/chat/control.js +104 -0
  33. package/dist/packages/autotrade/src/chat/fake.js +100 -0
  34. package/dist/packages/autotrade/src/chat/format.js +71 -0
  35. package/dist/packages/autotrade/src/chat/hub.js +187 -0
  36. package/dist/packages/autotrade/src/chat/index.js +18 -0
  37. package/dist/packages/autotrade/src/chat/notify.js +302 -0
  38. package/dist/packages/autotrade/src/chat/pairing.js +46 -0
  39. package/dist/packages/autotrade/src/chat/redact.js +48 -0
  40. package/dist/packages/autotrade/src/chat/secrets.js +106 -0
  41. package/dist/packages/autotrade/src/chat/service.js +251 -0
  42. package/dist/packages/autotrade/src/chat/telegram.js +290 -0
  43. package/dist/packages/autotrade/src/desk-executor.js +188 -0
  44. package/dist/packages/autotrade/src/engine.js +401 -0
  45. package/dist/packages/autotrade/src/execution-port.js +97 -0
  46. package/dist/packages/autotrade/src/executor-health.js +217 -0
  47. package/dist/packages/autotrade/src/fleet-config.js +169 -0
  48. package/dist/packages/autotrade/src/fleet-control.js +276 -0
  49. package/dist/packages/autotrade/src/fleet.js +642 -0
  50. package/dist/packages/autotrade/src/forward.js +355 -0
  51. package/dist/packages/autotrade/src/index.js +72 -0
  52. package/dist/packages/autotrade/src/journey.js +477 -0
  53. package/dist/packages/autotrade/src/ledger.js +108 -0
  54. package/dist/packages/autotrade/src/limits.js +682 -0
  55. package/dist/packages/autotrade/src/live-source.js +182 -0
  56. package/dist/packages/autotrade/src/monitor-daemon.js +715 -0
  57. package/dist/packages/autotrade/src/monitor.js +190 -0
  58. package/dist/packages/autotrade/src/ollama-sessions.js +137 -0
  59. package/dist/packages/autotrade/src/outbox.js +394 -0
  60. package/dist/packages/autotrade/src/paper-accounts.js +72 -0
  61. package/dist/packages/autotrade/src/paper-limits.js +87 -0
  62. package/dist/packages/autotrade/src/real-mode.js +688 -0
  63. package/dist/packages/autotrade/src/receipts.js +330 -0
  64. package/dist/packages/autotrade/src/replay.js +198 -0
  65. package/dist/packages/autotrade/src/report.js +168 -0
  66. package/dist/packages/autotrade/src/runner-config.js +99 -0
  67. package/dist/packages/autotrade/src/runner.js +923 -0
  68. package/dist/packages/autotrade/src/safety.js +243 -0
  69. package/dist/packages/autotrade/src/service-guard.js +125 -0
  70. package/dist/packages/autotrade/src/service-install.js +213 -0
  71. package/dist/packages/autotrade/src/service-recover.js +232 -0
  72. package/dist/packages/autotrade/src/service.js +289 -0
  73. package/dist/packages/autotrade/src/session.js +269 -0
  74. package/dist/packages/autotrade/src/sessions.js +503 -0
  75. package/dist/packages/autotrade/src/signal-channels.js +312 -0
  76. package/dist/packages/autotrade/src/signal.js +196 -0
  77. package/dist/packages/autotrade/src/store.js +250 -0
  78. package/dist/packages/autotrade/src/strategy.js +258 -0
  79. package/dist/packages/autotrade/src/trade-ledger.js +241 -0
  80. package/dist/packages/autotrade/src/yolo.js +140 -0
  81. package/dist/packages/cli/assets/claude/analyst-system.md +4 -4
  82. package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +1 -1
  83. package/dist/packages/cli/src/ai-cmd.js +412 -0
  84. package/dist/packages/cli/src/analyst/backend.js +83 -15
  85. package/dist/packages/cli/src/analyst/codex.js +15 -9
  86. package/dist/packages/cli/src/analyst/ollama.js +263 -0
  87. package/dist/packages/cli/src/analyst/session.js +5 -2
  88. package/dist/packages/cli/src/app-help-gen.js +56 -8
  89. package/dist/packages/cli/src/autotrade-cmd/audit.js +74 -0
  90. package/dist/packages/cli/src/autotrade-cmd/backtest.js +216 -0
  91. package/dist/packages/cli/src/autotrade-cmd/executor.js +91 -0
  92. package/dist/packages/cli/src/autotrade-cmd/flatten.js +64 -0
  93. package/dist/packages/cli/src/autotrade-cmd/fleet.js +401 -0
  94. package/dist/packages/cli/src/autotrade-cmd/forward.js +283 -0
  95. package/dist/packages/cli/src/autotrade-cmd/journey.js +208 -0
  96. package/dist/packages/cli/src/autotrade-cmd/limits.js +393 -0
  97. package/dist/packages/cli/src/autotrade-cmd/receipt.js +63 -0
  98. package/dist/packages/cli/src/autotrade-cmd/report.js +79 -0
  99. package/dist/packages/cli/src/autotrade-cmd/requests.js +79 -0
  100. package/dist/packages/cli/src/autotrade-cmd/run.js +657 -0
  101. package/dist/packages/cli/src/autotrade-cmd/service.js +873 -0
  102. package/dist/packages/cli/src/autotrade-cmd/signal.js +824 -0
  103. package/dist/packages/cli/src/autotrade-cmd/stop.js +76 -0
  104. package/dist/packages/cli/src/autotrade-cmd/strategies.js +198 -0
  105. package/dist/packages/cli/src/autotrade-cmd/strategy-control.js +174 -0
  106. package/dist/packages/cli/src/autotrade-cmd/telegram-host.js +101 -0
  107. package/dist/packages/cli/src/autotrade-cmd.js +214 -0
  108. package/dist/packages/cli/src/display.js +156 -17
  109. package/dist/packages/cli/src/doctor.js +62 -3
  110. package/dist/packages/cli/src/export.js +10 -2
  111. package/dist/packages/cli/src/index.js +1 -1
  112. package/dist/packages/cli/src/ledger-cmd.js +290 -0
  113. package/dist/packages/cli/src/live/chart-fills.js +46 -0
  114. package/dist/packages/cli/src/live/index.js +23 -89
  115. package/dist/packages/cli/src/live/screens/token-live.js +9 -6
  116. package/dist/packages/cli/src/live/trade.js +12 -457
  117. package/dist/packages/cli/src/live/watchlist.js +1 -1
  118. package/dist/packages/cli/src/main.js +147 -190
  119. package/dist/packages/cli/src/mcp-cmd.js +7 -1
  120. package/dist/packages/cli/src/plan-cmd.js +1 -1
  121. package/dist/packages/cli/src/pnl-app.js +31 -21
  122. package/dist/packages/cli/src/pnl-card.js +162 -21
  123. package/dist/packages/cli/src/popup.js +22 -6
  124. package/dist/packages/cli/src/record-cmd.js +141 -20
  125. package/dist/packages/cli/src/screens/agent-hud.js +34 -2
  126. package/dist/packages/cli/src/screens/analyst-session.js +7 -4
  127. package/dist/packages/cli/src/screens/analyst.js +3 -2
  128. package/dist/packages/cli/src/screens/autotrade-journey.js +134 -0
  129. package/dist/packages/cli/src/screens/autotrade-keys.js +182 -0
  130. package/dist/packages/cli/src/screens/autotrade-proposals.js +123 -0
  131. package/dist/packages/cli/src/screens/autotrade-screen.js +776 -0
  132. package/dist/packages/cli/src/screens/autotrade-ui.js +650 -0
  133. package/dist/packages/cli/src/screens/autotrade-worker.js +91 -0
  134. package/dist/packages/cli/src/screens/brain.js +37 -11
  135. package/dist/packages/cli/src/screens/overlay.js +5 -2
  136. package/dist/packages/cli/src/screens/plans.js +5 -17
  137. package/dist/packages/cli/src/screens/pnl-chip.js +9 -8
  138. package/dist/packages/cli/src/screens/positions.js +6 -28
  139. package/dist/packages/cli/src/screens/ticket.js +38 -205
  140. package/dist/packages/cli/src/screens/token.js +3 -3
  141. package/dist/packages/cli/src/state.js +25 -19
  142. package/dist/packages/cli/src/telegram-cmd.js +454 -0
  143. package/dist/packages/cli/src/trade.js +3 -128
  144. package/dist/packages/cli/src/version.js +1 -1
  145. package/dist/packages/cli/src/wallet-cmd.js +39 -340
  146. package/dist/packages/cli/src/wallet-command.js +8 -11
  147. package/dist/packages/cli/src/widgets.js +0 -12
  148. package/dist/packages/custom/src/config.js +20 -2
  149. package/dist/packages/custom/src/schema.js +35 -10
  150. package/dist/packages/custom/src/workspace.js +3 -3
  151. package/dist/packages/data/src/basis.js +191 -0
  152. package/dist/packages/data/src/detail.js +53 -2
  153. package/dist/packages/data/src/index.js +0 -14
  154. package/dist/packages/data/src/store.js +14 -5
  155. package/dist/packages/data/src/trade/agent/paper-desk.js +228 -16
  156. package/dist/packages/data/src/trade/agent/rendezvous.js +3 -2
  157. package/dist/packages/data/src/trade/bridge.js +4 -102
  158. package/dist/packages/data/src/trade/ledger/export.js +101 -0
  159. package/dist/packages/data/src/trade/ledger/file.js +135 -0
  160. package/dist/packages/data/src/trade/ledger/fold.js +191 -0
  161. package/dist/packages/data/src/trade/ledger/index.js +9 -0
  162. package/dist/packages/data/src/trade/ledger/schema.js +83 -0
  163. package/dist/packages/data/src/trade/ledger/sources.js +307 -0
  164. package/dist/packages/data/src/trade/ledger/stats.js +123 -0
  165. package/dist/packages/data/src/trade/ledger/tags.js +116 -0
  166. package/dist/packages/data/src/trade/ledger/view.js +51 -0
  167. package/dist/packages/data/src/trade/plans/index.js +0 -1
  168. package/dist/packages/data/src/trade/plans/service.js +3 -27
  169. package/dist/packages/mcp/assets/kb/app-help/autotrade-chat.md +48 -0
  170. package/dist/packages/mcp/assets/kb/app-help/autotrade-executor-kit.md +44 -0
  171. package/dist/packages/mcp/assets/kb/app-help/autotrade-journey.md +46 -0
  172. package/dist/packages/mcp/assets/kb/app-help/autotrade-limits.md +48 -0
  173. package/dist/packages/mcp/assets/kb/app-help/autotrade-parallel.md +42 -0
  174. package/dist/packages/mcp/assets/kb/app-help/autotrade-real-signal.md +45 -0
  175. package/dist/packages/mcp/assets/kb/app-help/autotrade-runner.md +35 -0
  176. package/dist/packages/mcp/assets/kb/app-help/autotrade-stop.md +37 -0
  177. package/dist/packages/mcp/assets/kb/app-help/autotrade-strategies.md +35 -0
  178. package/dist/packages/mcp/assets/kb/app-help/autotrade-telegram.md +40 -0
  179. package/dist/packages/mcp/assets/kb/app-help/chart-trade-markers.md +42 -0
  180. package/dist/packages/mcp/assets/kb/app-help/link-a-wallet.md +4 -10
  181. package/dist/packages/mcp/assets/kb/app-help/live-trading.md +38 -0
  182. package/dist/packages/mcp/assets/kb/app-help/live-yolo.md +34 -0
  183. package/dist/packages/mcp/assets/kb/app-help/order-plans.md +1 -1
  184. package/dist/packages/mcp/assets/kb/app-help/price-or-marketcap.md +5 -2
  185. package/dist/packages/mcp/assets/kb/app-help/screen-analyst.md +3 -0
  186. package/dist/packages/mcp/assets/kb/app-help/screen-autotrade.md +229 -0
  187. package/dist/packages/mcp/assets/kb/app-help/screen-kol.md +3 -0
  188. package/dist/packages/mcp/assets/kb/app-help/screen-newpairs.md +3 -0
  189. package/dist/packages/mcp/assets/kb/app-help/screen-positions.md +3 -0
  190. package/dist/packages/mcp/assets/kb/app-help/screen-screener.md +3 -0
  191. package/dist/packages/mcp/assets/kb/app-help/screen-token.md +3 -0
  192. package/dist/packages/mcp/assets/kb/app-help/screen-trending.md +3 -0
  193. package/dist/packages/mcp/assets/kb/app-help/screen-watchlist.md +3 -0
  194. package/dist/packages/mcp/assets/kb/app-help/standing-mandates.md +1 -1
  195. package/dist/packages/mcp/assets/kb/app-help/trade-ledger.md +54 -0
  196. package/dist/packages/mcp/assets/kb/app-help/use-local-model.md +38 -0
  197. package/dist/packages/mcp/assets/kb/docs/docs-agents-agent-paper-trading.md +4 -1
  198. package/dist/packages/mcp/assets/kb/docs/docs-agents-reading-your-trade-ledger.md +31 -0
  199. package/dist/packages/mcp/assets/kb/docs/docs-agents-untrusted-by-default.md +1 -1
  200. package/dist/packages/mcp/assets/kb/docs/docs-analyst-if-claude-is-missing.md +1 -1
  201. package/dist/packages/mcp/assets/kb/docs/docs-analyst-use-a-local-model-instead.md +27 -0
  202. package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-it-sees.md +2 -0
  203. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-backtests-and-their-limits.md +56 -0
  204. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-change-a-strategy-by-chat.md +51 -0
  205. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-checking-the-record-of-a-run.md +28 -0
  206. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-comparing-them.md +41 -0
  207. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-exits-and-wakes.md +76 -0
  208. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-how-a-stop-reaches-the-runner.md +32 -0
  209. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-on-the-q-screen.md +38 -0
  210. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-one-strategy-or-all-of-them.md +35 -0
  211. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-paper-accounts.md +50 -0
  212. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-presets.md +37 -0
  213. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-running-it-all-day.md +31 -0
  214. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-sessions-and-their-limits.md +31 -0
  215. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-several-strategies-at-once.md +33 -0
  216. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-stopping-it.md +37 -0
  217. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-strategies-and-presets.md +34 -0
  218. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-always-on-service.md +57 -0
  219. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-autotrade-json-file.md +48 -0
  220. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-heartbeat.md +27 -0
  221. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-run-file-and-the-heartbeat.md +34 -0
  222. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-the-runner-and-candidate-sessions.md +45 -0
  223. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-each-session-shows-first.md +31 -0
  224. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-costs-claude-usage-per-session.md +58 -0
  225. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-is-paper-self-directed.md +50 -0
  226. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-what-it-will-not-do.md +31 -0
  227. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-which-account-day-caps-and-the-usage-guard.md +44 -0
  228. package/dist/packages/mcp/assets/kb/docs/docs-autotrade-your-strategies.md +37 -0
  229. package/dist/packages/mcp/assets/kb/docs/docs-autotrade.md +22 -0
  230. package/dist/packages/mcp/assets/kb/docs/docs-beginners-before-any-experimental-trading.md +2 -2
  231. package/dist/packages/mcp/assets/kb/docs/docs-beginners.md +1 -1
  232. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-1-ask-for-a-strategy.md +27 -0
  233. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-2-backtest-it-held-out-split.md +31 -0
  234. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-3-forward-test-it-on-live-data.md +44 -0
  235. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-4-promote-it-to-always-on-paper.md +36 -0
  236. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-5-real-only-if-you-choose.md +36 -0
  237. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-the-ladder.md +39 -0
  238. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-the-report.md +31 -0
  239. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat-what-the-analyst-knows.md +30 -0
  240. package/dist/packages/mcp/assets/kb/docs/docs-build-a-strategy-by-chat.md +24 -0
  241. package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-markers.md +30 -0
  242. package/dist/packages/mcp/assets/kb/docs/docs-configuration-chart-y-axis.md +1 -1
  243. package/dist/packages/mcp/assets/kb/docs/docs-configuration-environment-variables.md +4 -1
  244. package/dist/packages/mcp/assets/kb/docs/docs-configuration-files-on-disk.md +1 -0
  245. package/dist/packages/mcp/assets/kb/docs/docs-configuration-headless-recorder-dexterm-record.md +3 -1
  246. package/dist/packages/mcp/assets/kb/docs/docs-configuration-price-display-mode.md +8 -6
  247. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-2-the-one-key-mirror-ticket-a-proposal.md +1 -1
  248. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-3-capped-auto-mirror-default-off-less.md +1 -1
  249. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-what-is-proven-what-is-not.md +1 -1
  250. package/dist/packages/mcp/assets/kb/docs/docs-copy-trading.md +1 -1
  251. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-recorded-history-and-retention.md +36 -0
  252. package/dist/packages/mcp/assets/kb/docs/docs-data-sources-sources.md +1 -0
  253. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-first-run.md +1 -1
  254. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-install.md +3 -3
  255. package/dist/packages/mcp/assets/kb/docs/docs-getting-started-requirements.md +1 -1
  256. package/dist/packages/mcp/assets/kb/docs/docs-getting-started.md +1 -1
  257. package/dist/packages/mcp/assets/kb/docs/docs-going-real-executor-health.md +38 -0
  258. package/dist/packages/mcp/assets/kb/docs/docs-going-real-paper-vs-real.md +33 -0
  259. package/dist/packages/mcp/assets/kb/docs/docs-going-real-the-digest.md +36 -0
  260. package/dist/packages/mcp/assets/kb/docs/docs-going-real-the-executor-kit.md +48 -0
  261. package/dist/packages/mcp/assets/kb/docs/docs-going-real-the-whole-path.md +37 -0
  262. package/dist/packages/mcp/assets/kb/docs/docs-going-real-what-dexterm-does-and-never-does.md +44 -0
  263. package/dist/packages/mcp/assets/kb/docs/docs-going-real-your-limits.md +77 -0
  264. package/dist/packages/mcp/assets/kb/docs/docs-going-real.md +24 -0
  265. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-changed-in-0-1-7.md +26 -0
  266. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-real-mode-your-strategy-s-trades-to-your.md +31 -0
  267. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-stopping.md +28 -0
  268. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-the-short-version.md +34 -0
  269. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-using-a-trading-bot-with-dexterm.md +31 -0
  270. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does-not-do.md +30 -0
  271. package/dist/packages/mcp/assets/kb/docs/docs-live-trading-what-dexterm-does.md +29 -0
  272. package/dist/packages/mcp/assets/kb/docs/docs-live-trading.md +23 -0
  273. package/dist/packages/mcp/assets/kb/docs/docs-ollama-another-machine.md +28 -0
  274. package/dist/packages/mcp/assets/kb/docs/docs-ollama-autotrade-on-a-local-model.md +29 -0
  275. package/dist/packages/mcp/assets/kb/docs/docs-ollama-check-your-model-dexterm-ai-probe.md +38 -0
  276. package/dist/packages/mcp/assets/kb/docs/docs-ollama-how-we-checked.md +27 -0
  277. package/dist/packages/mcp/assets/kb/docs/docs-ollama-models-without-tool-calling.md +32 -0
  278. package/dist/packages/mcp/assets/kb/docs/docs-ollama-pick-the-engine-globally-or-per-surface.md +43 -0
  279. package/dist/packages/mcp/assets/kb/docs/docs-ollama-restriction-profiles.md +35 -0
  280. package/dist/packages/mcp/assets/kb/docs/docs-ollama-set-it-up-in-two-minutes.md +39 -0
  281. package/dist/packages/mcp/assets/kb/docs/docs-ollama-troubleshooting.md +35 -0
  282. package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-runs-on-a-local-model.md +39 -0
  283. package/dist/packages/mcp/assets/kb/docs/docs-ollama-what-stays-outside-the-model.md +36 -0
  284. package/dist/packages/mcp/assets/kb/docs/docs-ollama-which-model.md +38 -0
  285. package/dist/packages/mcp/assets/kb/docs/docs-ollama-your-own-agent-on-the-local-model.md +28 -0
  286. package/dist/packages/mcp/assets/kb/docs/docs-ollama.md +28 -0
  287. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-letting-an-agent-paper-trade-experimental.md +3 -4
  288. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet-read-only.md +30 -0
  289. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-order-plans-and-standing-mandates.md +3 -4
  290. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-panic.md +4 -7
  291. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-paper-only-mandates.md +6 -7
  292. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-pnl-cards.md +7 -7
  293. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-safety-model.md +34 -0
  294. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-ticket.md +3 -4
  295. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-trade-ledger.md +42 -0
  296. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-wallet.md +4 -5
  297. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-trade-moments.md +4 -5
  298. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading.md +4 -5
  299. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-buy-and-sell-markers.md +37 -0
  300. package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-what-it-shows.md +2 -1
  301. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-receipts-what-your-executor-did.md +42 -0
  302. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-safety.md +34 -0
  303. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-set-it-up.md +47 -0
  304. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-the-channels.md +42 -0
  305. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-the-signal.md +48 -0
  306. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-what-it-is.md +35 -0
  307. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-what-it-will-not-do.md +34 -0
  308. package/dist/packages/mcp/assets/kb/docs/docs-real-mode-where-you-see-it.md +34 -0
  309. package/dist/packages/mcp/assets/kb/docs/docs-real-mode.md +27 -0
  310. package/dist/packages/mcp/assets/kb/docs/docs-screens-autotrade-paper.md +26 -0
  311. package/dist/packages/mcp/assets/kb/docs/docs-screens-order-plans.md +1 -1
  312. package/dist/packages/mcp/assets/kb/docs/docs-screens-token-screen.md +2 -0
  313. package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-brain-view-and-the-pnl-chip.md +1 -1
  314. package/dist/packages/mcp/assets/kb/docs/docs-telegram-approve-one-tap-entries.md +50 -0
  315. package/dist/packages/mcp/assets/kb/docs/docs-telegram-control-the-commands.md +43 -0
  316. package/dist/packages/mcp/assets/kb/docs/docs-telegram-if-something-goes-wrong.md +30 -0
  317. package/dist/packages/mcp/assets/kb/docs/docs-telegram-notify-what-reaches-your-phone.md +41 -0
  318. package/dist/packages/mcp/assets/kb/docs/docs-telegram-set-it-up.md +47 -0
  319. package/dist/packages/mcp/assets/kb/docs/docs-telegram-the-record.md +26 -0
  320. package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-is.md +33 -0
  321. package/dist/packages/mcp/assets/kb/docs/docs-telegram-what-it-will-not-do.md +31 -0
  322. package/dist/packages/mcp/assets/kb/docs/docs-telegram.md +23 -0
  323. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-common-questions.md +1 -1
  324. package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-doctor-lines.md +4 -0
  325. package/dist/packages/mcp/assets/kb/docs/docs-voice-linux-and-windows.md +1 -1
  326. package/dist/packages/mcp/assets/kb/docs/docs-voice.md +1 -1
  327. package/dist/packages/mcp/assets/kb/docs/docs-yolo-1-replay-it-on-your-own-history.md +42 -0
  328. package/dist/packages/mcp/assets/kb/docs/docs-yolo-2-run-it-on-paper.md +34 -0
  329. package/dist/packages/mcp/assets/kb/docs/docs-yolo-3-watch-it-and-stop-it.md +32 -0
  330. package/dist/packages/mcp/assets/kb/docs/docs-yolo-the-rules.md +37 -0
  331. package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-is.md +34 -0
  332. package/dist/packages/mcp/assets/kb/docs/docs-yolo-what-it-will-not-do.md +31 -0
  333. package/dist/packages/mcp/assets/kb/docs/docs-yolo.md +26 -0
  334. package/dist/packages/mcp/assets/kb/entries/autotrade.md +40 -0
  335. package/dist/packages/mcp/src/app-help.js +1 -1
  336. package/dist/packages/mcp/src/autotrade-tools.js +1107 -0
  337. package/dist/packages/mcp/src/backtest-requests.js +71 -0
  338. package/dist/packages/mcp/src/engine/config.js +159 -0
  339. package/dist/packages/mcp/src/engine/index.js +10 -0
  340. package/dist/packages/mcp/src/engine/loop.js +101 -0
  341. package/dist/packages/mcp/src/engine/mcp-client.js +123 -0
  342. package/dist/packages/mcp/src/engine/ollama.js +258 -0
  343. package/dist/packages/mcp/src/engine/probe.js +110 -0
  344. package/dist/packages/mcp/src/engine/reason.js +35 -0
  345. package/dist/packages/mcp/src/engine/restrictions.js +111 -0
  346. package/dist/packages/mcp/src/index.js +7 -1
  347. package/dist/packages/mcp/src/ledger-tools.js +494 -0
  348. package/dist/packages/mcp/src/paper-agent.js +2 -0
  349. package/dist/packages/mcp/src/server.js +138 -19
  350. package/dist/packages/packs/src/backtest.js +3 -1
  351. package/dist/packages/popup/src/pages/chart-lib.js +134 -21
  352. package/dist/packages/popup/src/pages/chart.css +9 -0
  353. package/dist/packages/popup/src/pages/chart.html +3 -1
  354. package/dist/packages/popup/src/pages/chart.js +104 -7
  355. package/dist/packages/popup/src/protocol.js +2 -0
  356. package/dist/packages/popup/src/server.js +4 -0
  357. package/dist/packages/recorder/src/index.js +2 -0
  358. package/dist/packages/recorder/src/recorder.js +63 -6
  359. package/dist/packages/recorder/src/retention.js +100 -0
  360. package/dist/packages/recorder/src/schema.js +34 -1
  361. package/dist/packages/recorder/src/ticks.js +237 -0
  362. package/dist/packages/tui/src/index.js +1 -0
  363. package/dist/packages/tui/src/keymap.js +28 -1
  364. package/dist/packages/tui/src/markers.js +219 -0
  365. package/dist/packages/tui/src/widgets.js +149 -2
  366. package/package.json +2 -1
  367. package/dist/packages/data/src/trade/caps.js +0 -323
  368. package/dist/packages/data/src/trade/executor.js +0 -933
  369. package/dist/packages/data/src/trade/plans/executor-live.js +0 -220
  370. package/dist/packages/data/src/trade/rpc-exec.js +0 -229
  371. package/dist/packages/data/src/trade/signer/client.js +0 -197
  372. package/dist/packages/data/src/trade/signer/fast-wallet.js +0 -154
  373. package/dist/packages/data/src/trade/signer/host.js +0 -97
  374. package/dist/packages/data/src/trade/signer/main.js +0 -121
  375. package/dist/packages/data/src/trade/signer/probe-builder.js +0 -85
  376. package/dist/packages/data/src/trade/signer/protocol.js +0 -92
  377. package/dist/packages/data/src/trade/signer/service.js +0 -400
  378. package/dist/packages/data/src/trade/signer/sol-usd-feed.js +0 -98
  379. package/dist/packages/data/src/trade/sol-usd.js +0 -86
  380. package/dist/packages/data/src/trade/swap-build.js +0 -108
  381. package/dist/packages/data/src/trade/token-delta.js +0 -201
  382. package/dist/packages/data/vendor/solana-web3/LICENSE +0 -20
  383. package/dist/packages/data/vendor/solana-web3/NOTICE.md +0 -13
  384. package/dist/packages/data/vendor/solana-web3/index.iife.min.js +0 -20
  385. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-a-live-ticket.md +0 -42
  386. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-before-you-press-size-vs-caps.md +0 -34
  387. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-devnet-signer-mainnet-token.md +0 -35
  388. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-jupiter-rate-limits.md +0 -33
  389. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet.md +0 -32
  390. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-live-trading-devnet-first.md +0 -30
  391. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-mainnet.md +0 -31
  392. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-live-fire-path-devnet-the-fast-wallet.md +0 -41
  393. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-signer-process.md +0 -34
  394. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-what-the-live-path-keeps.md +0 -37
  395. package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-your-first-live-trade.md +0 -41
@@ -44,6 +44,9 @@ import { OwnPnlTracker } from "./screens/pnl-chip.js";
44
44
  import { positionRow } from "./live/trade.js";
45
45
  import { widgetPage, widgetEnabled, syncWidgetPopups } from "./widgets.js";
46
46
  import { recordLarpVerdict } from "./larp-record.js";
47
+ import { newAutotradeUi, defaultDeps as autotradeDeps, autotradeCapturing, autotradeTick, reloadList as autotradeReload, refreshRunner as autotradeRefreshRunner } from "./screens/autotrade-ui.js";
48
+ import { autotradeAction, autotradeKey } from "./screens/autotrade-keys.js";
49
+ import { workerJobs as autotradeWorkerJobs } from "./screens/autotrade-worker.js";
47
50
  import { rulesUiOf, rulesTick, rulesMove, beginRuleInput, saveRuleInput, dryRunNow, removeSelectedRule, toggleSelectedRule, dismissSelectedProposal, actOnSelectedProposal, RULE_INPUT_MAX } from "./rules.js";
48
51
  import { buildNotifier, hasPushChannel } from "./alerts/notifier.js";
49
52
  import { copyAlertToDelivery } from "./alerts/format.js";
@@ -51,6 +54,7 @@ import { readTelegramCreds, readDiscordCreds } from "./alerts/creds.js";
51
54
  import { launchIntelCells, launchRetryable } from "./live/intel.js";
52
55
  import { AnalystSession } from "./analyst/session.js";
53
56
  import { analystCliOnPath, parseEngineChoice, resolveEngine } from "./analyst/codex.js";
57
+ import { analystEngineHas, analystAiChoice } from "./ai-cmd.js";
54
58
  import { resolveAnalystResults } from "./analyst/results.js";
55
59
  import { parseScanCommand, SCAN_FLAGS, checkScanFlag, SCAN_HELP } from "./scan/command.js";
56
60
  import { parseWalletCommand, WALLET_HELP } from "./wallet-command.js";
@@ -61,11 +65,11 @@ import { cleanText } from "./screener/rows.js";
61
65
  import { resolveImagesConfig } from "./images-config.js";
62
66
  import { popupImagePath } from "./screens/coin.js";
63
67
  import { VoiceController, selectStt, selectTts, SayTts, voiceStyleDefaults } from '../../voice/src/index.js';
64
- import { LivePlanExecutor, defaultPlansJournalPath } from '../../data/src/trade/plans/index.js';
68
+ import { defaultPlansJournalPath } from '../../data/src/trade/plans/index.js';
65
69
  import { isWalletAddress, shortAddr, buildMirror, leaderSoldSince, autoSizeCap } from '../../data/src/trade/copy/index.js';
66
70
  import { FOLLOW_INPUT_MAX } from "./screens/follows.js";
67
71
  import { HUD_MIN_H, hudAnimating } from "./screens/agent-hud.js";
68
- import { isPriceMode, configPriceMode, togglePriceMode, priceModeOf, planLevels, configChartAxis, setChartAxis, chartAxisOf } from "./display.js";
72
+ import { isPriceMode, configPriceMode, takeBasisFlags, togglePriceMode, setPriceMode, parseDisplayCommand, priceModeNotice, priceModeOf, planLevels, configChartAxis, setChartAxis, chartAxisOf, configChartMarkers, setChartMarkers, chartMarkersOn } from "./display.js";
69
73
  import { VERSION } from "./version.js";
70
74
  import { initCustom, planWorkspace, planPopupOpens, findWorkspace, nextWorkspaceName, workspaceSlots, saveWorkspace, themeFor, themeByName, cycleTheme, themeNames, CHORD_TTL_MS, MAX_WORKSPACE_NAME } from "./custom.js";
71
75
  import { DEFAULT_WORKSPACE, MORNING_HUD, applyAndJournal, policyCheck } from '../../custom/src/index.js';
@@ -135,7 +139,8 @@ export function buildScreenState(state) {
135
139
  const x = r;
136
140
  if (!x?.mint)
137
141
  return null;
138
- return { mint: x.mint, symbol: x.symbol ?? '', priceUsd: x.price ?? null, chg1h: x.chg?.h1 ?? null, chg24h: x.chg?.h24 ?? null, liq: x.liq ?? null, vol24: x.vol24 ?? null };
142
+ // task-1537: the row's market cap rides along (the analyst's context leads with it by default)
143
+ return { mint: x.mint, symbol: x.symbol ?? '', priceUsd: x.price ?? null, mcap: x.mcap ?? null, chg1h: x.chg?.h1 ?? null, chg24h: x.chg?.h24 ?? null, liq: x.liq ?? null, vol24: x.vol24 ?? null };
139
144
  };
140
145
  const toNewPairRow = (r) => {
141
146
  const x = r;
@@ -298,14 +303,17 @@ export const HELP = `dexterm ${VERSION} (live: screener, token, new pairs, watch
298
303
  dexterm update [--off|--on] check npm + GitHub for a newer version and print the upgrade command;
299
304
  --off/--on toggles the once-a-day startup notice (no telemetry)
300
305
  dexterm recorder stats|label|prune|export the history recorder's tables (no network; 'dexterm recorder help')
301
- dexterm kb list|show|new|edit|check|reset|path your knowledge-base entries (~/.dexterm/kb/entries) over the bundled 17
306
+ dexterm kb list|show|new|edit|check|reset|path your knowledge-base entries (~/.dexterm/kb/entries) over the bundled 18
302
307
  dexterm skill list|show|new|edit|check|reset|path your analyst skills (~/.dexterm/skills) over the bundled ones (read tools only)
303
308
  dexterm narrative [--window 1h|24h] [--summary] the narrative board over recorded history (no network, no model)
304
309
  dexterm backtest [--subject heuristic|pack_verdicts] [--horizon SEC] honest rug-model metrics over recorded outcomes (no network, no model)
305
- dexterm wallet link|unlink|status|panic|resume link a Phantom wallet via a local bridge (pubkey only); panic disables live trading, resume needs a TTY keypress
310
+ dexterm ledger trades|positions|summary|equity|compare|export|card|sync|verify the unified trade ledger: paper, agent desk, autotrade runs (no network; 'dexterm ledger help')
311
+ dexterm wallet link|unlink|status|panic|resume link a Phantom wallet via a local bridge (pubkey only); panic halts autotrade + the paper desk, resume needs a TTY keypress
306
312
  dexterm plan propose|list|arm|cancel|journal conditional order plans (entry/ladder/take-profit/moon bag/stop); arming needs a physical keypress
307
- dexterm mandate draft|list|arm|revoke standing capped mandates (fast wallet); arming needs a physical keypress — voice/agents can never arm
313
+ dexterm mandate draft|list|arm|revoke standing capped mandates (paper fills); arming needs a physical keypress — voice/agents can never arm
308
314
  dexterm follow add|remove|list|stats|trades|mirror|purge copy trading tier 1: follow a wallet (your list, local labels), its decoded swaps + FIFO PnL + win rate/early-entry; 'mirror' previews a one-key MIRROR TICKET from the wallet's latest buy (inert; arm in the app)
315
+ dexterm ai [status|engine|model|host|restrictions|probe|ask|agent] the AI engine: your claude / codex CLI or a free LOCAL model on
316
+ your own Ollama (localhost); restriction profiles observe|propose|paper|full ('dexterm ai help')
309
317
  dexterm mcp [install <client>|doctor] read-only MCP server for your own agent; 'install' writes the config for claude-code|claude-desktop|cursor|codex|generic (asks first, idempotent, backs up); 'doctor' verifies the connection
310
318
  dexterm alerts status|enable|disable|set-telegram|set-discord|test where rule alerts are delivered (desktop / your Telegram / Discord; creds stay local, never logged)
311
319
  dexterm watch [--interval S] [--once] [--replay FILE] run your rules headless and push alerts when the terminal is closed (no signer, proposals only)
@@ -344,8 +352,9 @@ export const HELP = `dexterm ${VERSION} (live: screener, token, new pairs, watch
344
352
  --selected N highlighted row / token index
345
353
  --seed N fixture PRNG seed (default 7)
346
354
  --no-ai hide the analyst panel on the token screen
347
- --analyst-engine E auto | claude | codex: which of YOUR installed CLIs runs the analyst (default auto = claude
348
- if installed, else codex; DEXTERM_ANALYST_ENGINE or config analyst.engine too; /engine in chat)
355
+ --analyst-engine E auto | claude | codex | ollama: which of YOUR engines runs the analyst (default auto = claude
356
+ if installed, else codex, else a local Ollama model; DEXTERM_ANALYST_ENGINE, config analyst.engine
357
+ or ai.json too; /engine in chat). ollama = a free local model, see 'dexterm ai help'
349
358
 
350
359
  --intensity LEVEL off | normal | fever (default fever: rain, trails, glitch, blink, scanlines at 30 fps;
351
360
  normal: 15 fps, sparser rain, no idle aberration; off = calm: neon theme, no motion)
@@ -364,12 +373,16 @@ export const HELP = `dexterm ${VERSION} (live: screener, token, new pairs, watch
364
373
  result only; muted by default (also DEXTERM_SOUND=1); ~/.dexterm/sounds holds the WAVs
365
374
  --workspace NAME start in a workspace: default | morning-hud | a file in ~/.dexterm/workspaces/ (task-1358);
366
375
  in the app ctrl+w then w (list) · n/N (next/prev) · s (save as) · m (morning HUD) · o (popups) · d · 1-9
367
- --price-mode MODE price | mcap: show USD price or MARKET CAP (price × supply) on the tables, token screen and both
368
- charts for this run (default: config.json display.priceMode; \$ in the app toggles and saves it)
376
+ --price-mode MODE mcap | price: show MARKET CAP (price × a known supply; the default) or USD price on every screen,
377
+ the CLI text and in the assistants' language for this run (default: config.json display.priceMode,
378
+ mcap; \$ in the app toggles and saves it). --mcap / --price (any command) and
379
+ DEXTERM_PRICE_BASIS=mcap|price do the same for one run
380
+ --mcap / --price the price basis for this run on ANY command (dexterm --price autotrade run …), not saved
369
381
  dexterm workspace list|show|remove|init the workspace files (no network)
370
382
  dexterm theme list|show|init|check user themes in ~/.dexterm/themes (extends a built-in; contrast + hue guarded)
371
383
  dexterm keymap [screen] the effective keys (defaults + config.keymap overrides, marked ·)
372
- dexterm display [price|mcap] show or set the price display mode (market cap = price × supply)
384
+ dexterm display [mcap|price] show or set the price basis (default market cap = price × a known supply)
385
+ dexterm display markers [on|off] show or hide the BUY/SELL markers of your fills on both charts
373
386
  dexterm rules list|add|remove|enable|disable|check|dry-run|fields
374
387
  "when <cond> [for n ticks] then alert|ticket|ask|popup|tag|sound|watch" rules in config.json (task-1358);
375
388
  parsed never eval'd; mandatory cooldown + max/h; rules only alert or PROPOSE (ctrl+r in the app)
@@ -382,14 +395,10 @@ export const HELP = `dexterm ${VERSION} (live: screener, token, new pairs, watch
382
395
  AI SCAN MODE (off by default; observe + analyze only — it can never open a ticket, sign or trade):
383
396
  ${SCAN_HELP}
384
397
 
385
- LIVE TRADING (task-1328; devnet by default; a separate signer process holds the caps and signs via Phantom):
386
- --mainnet allow MAINNET swaps (real funds) within the caps; without it the signer refuses mainnet
387
- --max-usd N per-trade USD cap (default $5; raising it needs --mainnet; hard ceiling $500)
388
- --daily-usd N daily USD cap (default $20; raising it needs --mainnet; hard ceiling $2000)
389
- --max-sol-out N SOL that may leave the wallet in one tx (default 1, ceiling 5)
390
- --max-slippage-bps N slippage ceiling (default 500, ceiling 1000)
391
- --trade-rpc URL the signer's RPC (default devnet; must be devnet unless --mainnet)
392
- --no-live paper only: never start the signer process
398
+ WALLET + LIVE TRADING (dexterm never holds a key and never signs a transaction):
399
+ live trading happens in the Telegram trading bot of your choice, which holds its own wallet; dexterm sends alerts,
400
+ signals and run control to your chat through your own bot (dexterm telegram setup). dexterm places no live order.
401
+ --no-live accepted for older scripts; changes nothing (trading in dexterm is paper only)
393
402
  ${WALLET_HELP}
394
403
 
395
404
  LOGIN WITH X (task-1357; your own X app, your limits, your bill; read scopes only, no scraping, dexterm never posts):
@@ -414,17 +423,15 @@ ${X_HELP}
414
423
  social (task-1330): T trending in the space · K KOL lists · token screen: s social panel · f filter · x hidden spam
415
424
  dexterm kol list | create <name> | add <list> @handle | remove <list> @handle | mute @handle | unmute @handle | enable <list> | disable <list> | import <dir> | import-x
416
425
  Login with X (task-1357): dexterm x login · then the SOCIAL panel + TRENDING read X with your own limits · KOL screen: X imports your X lists · o = only this handle
417
- token: B/S buy/sell ticket (PAPER; L on the ticket flips LIVE when a wallet is linked) · P positions · w watch
426
+ token: B/S buy/sell ticket (PAPER) · P positions · w watch
418
427
  token: M bubble map (holder graph: clusters by funder / same-slot buys / transfers; colour = rule) · bubble map: M popup · m treemap · z zoom · D deep scan
419
428
  demo events on --fixtures: n new pair (glitch) · u pump (gold rain) · d dump (red rain) · x rug flag
420
429
  AI scan: Z on/off · R resume scanning after a lock · esc (on the locked coin) cancel the analysis
421
430
  any key skips the boot sequence or a blink transition
422
431
 
423
432
  State: ~/.dexterm/state.json remembers launches (DEXTERM_HOME overrides the directory).
424
- DEXTERM_OFFLINE=1 refuses live data (tests and CI). Trading is PAPER by default — a simulated wallet
425
- priced from live Jupiter quotes. LIVE (L on a ticket, /wallet link first): a separate signer process
426
- quotes, builds, decodes, simulates and enforces the caps; Phantom signs in your browser; dexterm never
427
- holds a key. The analyst, voice and scan mode can never open, confirm or sign a live trade.
433
+ DEXTERM_OFFLINE=1 refuses live data (tests and CI). Trading in dexterm is PAPER only — a simulated wallet
434
+ priced from live Jupiter quotes. dexterm never holds a key and never signs or sends a transaction.
428
435
  RPC: DEXTERM_RPC_URL, else ~/.dexterm/helius.key (Helius, metered with a credit meter), else the public RPC.
429
436
  Updates: at most once a day a live run asks npm + GitHub for the newest version (two anonymous GETs, no telemetry)
430
437
  and shows one toast if there is one; DEXTERM_NO_UPDATE_CHECK=1 or 'dexterm update --off' turns that off.`;
@@ -438,7 +445,6 @@ export function parseArgs(argv) {
438
445
  scan: false, scanConfig: {},
439
446
  noRecorder: false,
440
447
  sound: false,
441
- trade: [], live: true,
442
448
  workspace: null, priceMode: null,
443
449
  spectateMcp: false, spectateThoughts: null, agentPaper: null,
444
450
  help: false, version: false, unknown: [],
@@ -533,7 +539,7 @@ export function parseArgs(argv) {
533
539
  const s = str();
534
540
  const e = parseEngineChoice(s);
535
541
  if (!e)
536
- throw new Error(`unknown analyst engine ${s} (expected auto|claude|codex)`);
542
+ throw new Error(`unknown analyst engine ${s} (expected auto|claude|codex|ollama)`);
537
543
  a.analystEngine = e;
538
544
  }
539
545
  else if (k === '--calm')
@@ -611,18 +617,10 @@ export function parseArgs(argv) {
611
617
  a.noRecorder = true;
612
618
  else if (k === '--sound')
613
619
  a.sound = true;
614
- // task-1328 LIVE trading flags: forwarded verbatim to the signer process, which validates them
615
- // (over-ceiling values are refused there; without --mainnet the USD caps only go down).
616
- else if (k === '--mainnet')
617
- a.trade.push(k);
618
- else if (k === '--max-usd' || k === '--daily-usd' || k === '--max-sol-out' || k === '--max-slippage-bps') {
619
- a.trade.push(k, String(num()));
620
- }
621
- else if (k === '--trade-rpc') {
622
- a.trade.push('--rpc', str());
623
- }
624
- else if (k === '--no-live')
625
- a.live = false;
620
+ // task-1571: dexterm has no live trading of its own; `--no-live` (0.1.6) is accepted and changes nothing.
621
+ else if (k === '--no-live') { /* paper only is the only mode */ }
622
+ else if (k === '--price' || k === '--mcap')
623
+ a.priceMode = k === '--price' ? 'price' : 'mcap'; // task-1537 (main() also takes them before any subcommand)
626
624
  else if (k === '--price-mode') {
627
625
  const s = str();
628
626
  if (!isPriceMode(s))
@@ -831,6 +829,8 @@ export function fastLaneEndpoints(rpc) {
831
829
  return out;
832
830
  }
833
831
  export async function main(argv, io = process, proc = process) {
832
+ // task-1537: `--price` / `--mcap` anywhere = the price basis for this run (every screen, CLI text and agent prompt).
833
+ argv = takeBasisFlags(argv, io.env);
834
834
  // `dexterm mcp` → the read-only MCP server on stdio (SPEC §5.1; `claude mcp add dexterm -- dexterm mcp`),
835
835
  // plus `dexterm mcp install <client>` (one-command setup) and `dexterm mcp doctor` (verify the connection).
836
836
  if (argv[0] === 'mcp') {
@@ -859,6 +859,17 @@ export async function main(argv, io = process, proc = process) {
859
859
  const { narrativeCommand } = await import("./narrative-cmd.js");
860
860
  return narrativeCommand(argv.slice(1), { stdout: io.stdout, stderr: io.stderr, env: io.env });
861
861
  }
862
+ // `dexterm ledger …` → the unified trade ledger (task-1530): reads fold the file + the sources; only `sync` writes. No network, no model, no keys.
863
+ if (argv[0] === 'ledger') {
864
+ const { ledgerCommand } = await import("./ledger-cmd.js");
865
+ return ledgerCommand(argv.slice(1), { stdout: io.stdout, stderr: io.stderr, env: io.env });
866
+ }
867
+ // `dexterm autotrade validate|list|backtest|sweep …` and `dexterm backtest --subject strategy` → the paper autotrade engine (task-1503). No network, no model, no keys.
868
+ if (argv[0] === 'autotrade' || argv[0] === 'backtest') {
869
+ const at = await import("./autotrade-cmd.js");
870
+ if (at.handles(argv))
871
+ return at.autotradeCommand(argv, { stdout: io.stdout, stderr: io.stderr, env: io.env, stdin: io.stdin });
872
+ }
862
873
  // `dexterm backtest [--subject heuristic|pack_verdicts] [--horizon SEC]` → honest rug-model metrics over recorded outcomes (task-1356). No network, no model.
863
874
  if (argv[0] === 'backtest') {
864
875
  const { backtestCommand } = await import("./backtest-cmd.js");
@@ -921,6 +932,12 @@ export async function main(argv, io = process, proc = process) {
921
932
  const { alertsCommand } = await import("./alerts-cmd.js");
922
933
  return alertsCommand(argv.slice(1), { stdout: io.stdout, stderr: io.stderr, env: io.env, stdin: io.stdin });
923
934
  }
935
+ // `dexterm telegram setup|pair|status|approvals|notify|set|test|audit|revoke` → your own bot for the autotrade runner
936
+ // (task-1527). The token goes in the OS secret store (or a 0600 file), never config/argv/env/logs.
937
+ if (argv[0] === 'telegram') {
938
+ const { telegramCommand } = await import("./telegram-cmd.js");
939
+ return telegramCommand(argv.slice(1), { stdout: io.stdout, stderr: io.stderr, env: io.env, stdin: io.stdin });
940
+ }
924
941
  // `dexterm watch [--interval S] [--once|--ticks N] [--replay FILE]` → run the rules engine headless and
925
942
  // PUSH alerts to the enabled channels so they reach you when the terminal is closed (task-1398). No signer.
926
943
  if (argv[0] === 'watch') {
@@ -943,6 +960,11 @@ export async function main(argv, io = process, proc = process) {
943
960
  }
944
961
  // `dexterm wallet <link|unlink|status>` → link a Phantom wallet via the loopback bridge (keys never
945
962
  // enter dexterm; only the pubkey is stored), unlink it, or show the link + trade caps (task-1310 T1).
963
+ // task-1571: `dexterm live …` (the 0.1.7 local signer) was removed before release. dexterm holds no key.
964
+ if (argv[0] === 'live') {
965
+ io.stderr.write('dexterm live: not available. dexterm never holds a key and never signs; live trading happens in the Telegram trading bot of your choice (dexterm telegram setup sends alerts and run control to your chat)\n');
966
+ return 2;
967
+ }
946
968
  if (argv[0] === 'wallet') {
947
969
  const { runWallet } = await import("./wallet-cmd.js");
948
970
  return runWallet(argv.slice(1), io);
@@ -974,6 +996,11 @@ export async function main(argv, io = process, proc = process) {
974
996
  const { voiceCommand } = await import("./voice-cmd.js");
975
997
  return voiceCommand(argv.slice(1), { stdin: io.stdin, stdout: io.stdout, stderr: io.stderr, env: io.env });
976
998
  }
999
+ // task-1543: `dexterm ai …` (engine, local model, restriction profile, probe) and `dexterm ask` (a headless question).
1000
+ if (argv[0] === 'ai' || argv[0] === 'ask') {
1001
+ const { aiCommand } = await import("./ai-cmd.js");
1002
+ return aiCommand(argv[0] === 'ask' ? ['ask', ...argv.slice(1)] : argv.slice(1), { stdin: io.stdin, stdout: io.stdout, stderr: io.stderr, env: io.env });
1003
+ }
977
1004
  // `dexterm doctor` → a pre-flight that says whether dexterm will run here and how to fix what won't (task-1305).
978
1005
  if (argv[0] === 'doctor') {
979
1006
  const { defaultDeps, runDoctor, renderDoctor } = await import("./doctor.js");
@@ -1069,9 +1096,8 @@ export async function main(argv, io = process, proc = process) {
1069
1096
  });
1070
1097
  ctx = new LiveContext(feed.dl, {
1071
1098
  mode: 'live', dbPath: dbOpt, rpc: { provider: rpc.provider, basis: rpc.basis }, record: args.record ? feed.recording ?? undefined : undefined, interval: args.interval, fastEndpoints: fastLaneEndpoints(rpc), onLog: args.verbose ? (l) => io.stderr.write(l) : undefined, allowedSigners: join(dextermHome(io.env), 'allowed_signers'),
1072
- // task-1328: LIVE trading is available in a real live run (never replay/offline); the signer
1073
- // process starts lazily on the first LIVE action with exactly these flags.
1074
- ...(args.live && !io.env.DEXTERM_OFFLINE ? { trade: { signerArgv: args.trade } } : {}),
1099
+ // The wallet link (public key only) is available in a real live run (never replay/offline).
1100
+ ...(!io.env.DEXTERM_OFFLINE ? { trade: {} } : {}),
1075
1101
  });
1076
1102
  // task-1329: the history recorder listens to the data layer's normaliser (zero requests of its own)
1077
1103
  // and keeps snapshots / feed membership / outcome labels in the same --db file. Live mode only (a
@@ -1089,7 +1115,7 @@ export async function main(argv, io = process, proc = process) {
1089
1115
  ctx.newpairs.onCreator = (mint, creator, source, createdAt) => { recorder.recordCreator(mint, creator, source, undefined, createdAt); };
1090
1116
  }
1091
1117
  if (args.verbose)
1092
- io.stderr.write(`dexterm: rpc ${rpc.provider} (${rpc.basis}) · db ${dbPath} · recorder ${ctx.recorder ? 'on' : 'off'} · live trading ${args.live && !io.env.DEXTERM_OFFLINE ? 'available' : 'off'}\n`);
1118
+ io.stderr.write(`dexterm: rpc ${rpc.provider} (${rpc.basis}) · db ${dbPath} · recorder ${ctx.recorder ? 'on' : 'off'} · paper trading only (dexterm holds no key)\n`);
1093
1119
  }
1094
1120
  if (args.preset && !feed.setPreset(args.preset)) {
1095
1121
  const v = feed.presets().find((p) => p.id === args.preset);
@@ -1151,8 +1177,11 @@ export async function main(argv, io = process, proc = process) {
1151
1177
  }
1152
1178
  for (const e of state.custom.themeErrors)
1153
1179
  io.stderr.write(`dexterm: ${e}\n`);
1154
- state.priceMode = args.priceMode ?? configPriceMode(state.custom.config); // task-1366: --price-mode wins for the run
1180
+ if (args.priceMode)
1181
+ io.env.DEXTERM_PRICE_BASIS = args.priceMode; // task-1537: the agents follow the run's basis
1182
+ state.priceMode = args.priceMode ?? configPriceMode(state.custom.config, io.env); // task-1366: --price-mode wins for the run; task-1537: then --price/--mcap/env, config, mcap
1155
1183
  state.chartAxis = configChartAxis(state.custom.config); // task-1472: default MARKET CAP, `A` toggles + saves
1184
+ state.chartMarkers = configChartMarkers(state.custom.config); // task-1483: BUY/SELL markers on both charts (default on)
1156
1185
  if (args.workspace) {
1157
1186
  const r = switchWorkspace(state, args.workspace, []);
1158
1187
  if (!r.ok) {
@@ -1385,6 +1414,18 @@ function followsMove(state, delta) {
1385
1414
  state.copyUi.selected = Math.max(0, Math.min(n - 1, state.copyUi.selected + delta));
1386
1415
  return true;
1387
1416
  }
1417
+ // task-1508: open (or return to) the AUTOTRADE screen; the strategy list and the runner status are re-read each time.
1418
+ function openAutotrade(state, stack) {
1419
+ if (state.screen !== 'autotrade')
1420
+ stack.push(state.screen);
1421
+ state.screen = 'autotrade';
1422
+ if (!state.autotradeUi) {
1423
+ state.autotradeUi = newAutotradeUi(autotradeDeps(process.env, autotradeWorkerJobs(process.env)));
1424
+ return;
1425
+ }
1426
+ autotradeReload(state.autotradeUi);
1427
+ autotradeRefreshRunner(state.autotradeUi);
1428
+ }
1388
1429
  // task-1358 Tier 3: j/k on the RULES screen move the cursor of the active pane (frozen while a rule is being typed).
1389
1430
  function rulesScreenMove(state, delta) {
1390
1431
  if (state.screen !== 'rules' || !state.rulesUi)
@@ -1395,7 +1436,7 @@ function rulesScreenMove(state, delta) {
1395
1436
  function plansMove(state, delta) {
1396
1437
  if (state.screen !== 'plans' || !state.plansUi)
1397
1438
  return false;
1398
- if (state.plansUi.arming || state.plansUi.riskModal || state.plansUi.liveModal)
1439
+ if (state.plansUi.arming || state.plansUi.riskModal)
1399
1440
  return true;
1400
1441
  const n = plansRows(state).length;
1401
1442
  if (n <= 0) {
@@ -1441,6 +1482,14 @@ export function applyAction(state, action, stack) {
1441
1482
  }
1442
1483
  return null;
1443
1484
  };
1485
+ // task-1508 AUTOTRADE (paper): `Q` opens the screen; while it is open it handles its own verbs and movement
1486
+ // (esc on its strategies list falls through to the normal back). Nothing it does reaches live mode or the signer.
1487
+ if (action === 'screen.autotrade') {
1488
+ openAutotrade(state, stack);
1489
+ return;
1490
+ }
1491
+ if (state.screen === 'autotrade' && state.autotradeUi && autotradeAction(state.autotradeUi, action))
1492
+ return;
1444
1493
  switch (action) {
1445
1494
  case 'quit': return 'quit';
1446
1495
  case 'screen.screener':
@@ -2090,24 +2139,6 @@ export function applyAction(state, action, stack) {
2090
2139
  }
2091
2140
  break;
2092
2141
  }
2093
- case 'plans.live': {
2094
- // task-1363: raise the FIRE PATH modal (paper ⇄ live devnet fast wallet). Exactly like arm and gate this
2095
- // ONLY sets a modal flag; the switch itself happens in the interactive key loop on a captured physical
2096
- // keypress, and PlanService.useLive refuses any gesture but 'keypress' — so applyAction (a pipe, an
2097
- // agent, the voice router) can never move fills off paper.
2098
- if (state.screen !== 'plans')
2099
- break;
2100
- const svc = state.plansSvc;
2101
- if (!svc)
2102
- break;
2103
- if (state.plansUi) {
2104
- state.plansUi.arming = null;
2105
- state.plansUi.riskModal = false;
2106
- state.plansUi.liveModal = true;
2107
- state.plansUi.notice = null;
2108
- }
2109
- break;
2110
- }
2111
2142
  // task-1360 copy trading Tier 1: the FOLLOWED WALLETS screen. Opening it binds the follow store (+ the live
2112
2143
  // tracker when there is one). `follows.add` only opens the typed-address line (the loop captures the keys);
2113
2144
  // `follows.remove` unfollows the selected row. Nothing here trades, proposes or signs.
@@ -2718,11 +2749,12 @@ export function applyScanAnalystAction(state, raw) {
2718
2749
  * The analyst engine for a run (0.1.5): `--analyst-engine`, then DEXTERM_ANALYST_ENGINE, then config
2719
2750
  * `analyst.engine`, then auto (claude when installed, else codex). Only the user picks it; models cannot.
2720
2751
  */
2721
- export function analystEngineFor(flag, env, configured, has = (b) => analystCliOnPath(b, env)) {
2722
- const choice = parseEngineChoice(flag) ?? parseEngineChoice(env.DEXTERM_ANALYST_ENGINE) ?? parseEngineChoice(configured) ?? 'auto';
2752
+ export function analystEngineFor(flag, env, configured, has = (b) => analystCliOnPath(b, env), aiChoice) {
2753
+ // task-1543: ai.json (provider / surfaces.analyst / DEXTERM_AI_PROVIDER) comes after the analyst's own settings.
2754
+ const choice = parseEngineChoice(flag) ?? parseEngineChoice(env.DEXTERM_ANALYST_ENGINE) ?? parseEngineChoice(configured) ?? parseEngineChoice(aiChoice) ?? 'auto';
2723
2755
  return resolveEngine(choice, has);
2724
2756
  }
2725
- /** `/engine [auto|claude|codex]` in the chat box (0.1.5). Null when the input is not this command. */
2757
+ /** `/engine [auto|claude|codex|ollama]` in the chat box (0.1.5; ollama task-1543). Null when the input is not this command. */
2726
2758
  export function parseEngineCommand(q) {
2727
2759
  const m = /^\/engine(?:\s+(\S+))?\s*$/i.exec(q.trim());
2728
2760
  if (!m)
@@ -2730,23 +2762,24 @@ export function parseEngineCommand(q) {
2730
2762
  if (!m[1])
2731
2763
  return { show: true };
2732
2764
  const c = parseEngineChoice(m[1]);
2733
- return c ? { choice: c } : { error: `unknown analyst engine "${m[1].slice(0, 16)}" (use /engine auto|claude|codex)` };
2765
+ return c ? { choice: c } : { error: `unknown analyst engine "${m[1].slice(0, 16)}" (use /engine auto|claude|codex|ollama)` };
2734
2766
  }
2735
2767
  /**
2736
2768
  * Apply `/engine`: switch the CLI for NEW conversations this run (cached per-coin chats are dropped so the next
2737
2769
  * `a` starts on the chosen engine; a running turn finishes where it started). Returns the line to show.
2738
2770
  */
2739
- export function engineCommand(state, cmd, env, has = (b) => analystCliOnPath(b, env)) {
2740
- const installed = ['claude', 'codex'].filter((b) => has(b));
2741
- const list = installed.length ? installed.join(' + ') : 'neither claude nor codex';
2771
+ export function engineCommand(state, cmd, env, has = analystEngineHas(env)) {
2772
+ const installed = ['claude', 'codex', 'ollama'].filter((b) => has(b));
2773
+ const list = installed.length ? installed.join(' + ') : 'no AI engine yet';
2742
2774
  if ('error' in cmd)
2743
2775
  return cmd.error;
2744
2776
  const cur = state.analystEngine ?? 'claude';
2745
2777
  if ('show' in cmd)
2746
- return `analyst engine: ${cur} · installed: ${list} · /engine claude|codex|auto switches (config analyst.engine keeps it)`;
2778
+ return `analyst engine: ${cur} · installed: ${list} · /engine claude|codex|ollama|auto switches (config analyst.engine keeps it)`;
2747
2779
  const next = resolveEngine(cmd.choice, has);
2780
+ const how = next === 'codex' ? 'npm i -g @openai/codex && codex login' : next === 'ollama' ? 'install Ollama (https://ollama.com/download), `ollama pull qwen3:8b`, `dexterm ai engine ollama`' : 'install Claude Code, then run claude once to log in';
2748
2781
  if (cmd.choice !== 'auto' && !has(next))
2749
- return `${next} is not installed (installed: ${list}); ${next === 'codex' ? 'npm i -g @openai/codex && codex login' : 'install Claude Code, then run claude once to log in'}`;
2782
+ return `${next} is not ${next === 'ollama' ? 'set up' : 'installed'} (installed: ${list}); ${how}`;
2750
2783
  if (next !== cur) {
2751
2784
  for (const [k, sess] of state.analystByMint ?? []) {
2752
2785
  if (!sess.busy)
@@ -2756,6 +2789,8 @@ export function engineCommand(state, cmd, env, has = (b) => analystCliOnPath(b,
2756
2789
  state.analyst = null;
2757
2790
  }
2758
2791
  state.analystEngine = next;
2792
+ if (next === 'ollama')
2793
+ return 'analyst engine: ollama (a local model on your own Ollama, $0) for new conversations · `dexterm ai engine ollama` keeps it';
2759
2794
  return `analyst engine: ${next} (your own ${next} CLI and login) for new conversations · set analyst.engine in config.json to keep it`;
2760
2795
  }
2761
2796
  /**
@@ -2771,7 +2806,7 @@ export function createScanSession(state, mint, symbol, hooks = {}, deps = {}) {
2771
2806
  const type = typeof raw.type === 'string' ? raw.type.replace(/[^a-z_]/gi, '').slice(0, 24) : '?';
2772
2807
  hooks.onBlocked?.(type, r.error ?? 'refused');
2773
2808
  };
2774
- return new AnalystSession({ skill: 'token-dossier', mint, symbol, screenState: ss, onAction, actionTypes: SCAN_ACTION_TYPES, scan: true, ...(state.analystEngine === 'codex' ? { engine: 'codex' } : {}) }, hooks.onChange ?? (() => { }), deps);
2809
+ return new AnalystSession({ skill: 'token-dossier', mint, symbol, screenState: ss, onAction, actionTypes: SCAN_ACTION_TYPES, scan: true, ...(state.analystEngine && state.analystEngine !== 'claude' ? { engine: state.analystEngine } : {}) }, hooks.onChange ?? (() => { }), deps);
2775
2810
  }
2776
2811
  /**
2777
2812
  * Resolve a mint to an openable seed from what the app already knows (screener view → new pairs →
@@ -3069,7 +3104,7 @@ export function ownHeldSample(state, now) {
3069
3104
  if (!ownOpenedMemo || ownOpenedMemo.mint !== mint || now - ownOpenedMemo.at > 5000) {
3070
3105
  let t = null;
3071
3106
  try {
3072
- t = currentPositionFills([...(live.livePositions?.records(200, mint) ?? []), ...live.wallet.trades(500).filter((x) => x.mint === mint)])[0]?.t ?? null;
3107
+ t = currentPositionFills(live.wallet.trades(500).filter((x) => x.mint === mint))[0]?.t ?? null;
3073
3108
  }
3074
3109
  catch {
3075
3110
  t = null;
@@ -3077,7 +3112,7 @@ export function ownHeldSample(state, now) {
3077
3112
  ownOpenedMemo = { mint, at: now, t };
3078
3113
  }
3079
3114
  return {
3080
- mint, symbol: held.symbol, kind: held.kind === 'live' ? 'live' : 'paper',
3115
+ mint, symbol: held.symbol, kind: 'paper',
3081
3116
  entryUsd: r.avgEntryUsd, markUsd: r.priceUsd, pnlPct: r.unrealizedPct, pnlUsd: r.unrealizedUsd,
3082
3117
  openedAt: ownOpenedMemo.t, targetUsd: r.targetUsd,
3083
3118
  };
@@ -3330,7 +3365,7 @@ export function applyKey(state, key, stack) {
3330
3365
  popups.push('plans');
3331
3366
  const widgets = (p?.openWidgets?.() ?? []).map((w) => w.key); // task-1358 Tier 5: open pack widgets are saved by ref
3332
3367
  const entry = saveWorkspace(ui, {
3333
- name, screen: state.screen, theme: state.theme.name, intensity: state.vfx.intensity, priceMode: state.priceMode ?? 'price',
3368
+ name, screen: state.screen, theme: state.theme.name, intensity: state.vfx.intensity, priceMode: priceModeOf(state),
3334
3369
  screener: { preset: sc.preset, filter: sc.query ?? null, sort: `${sc.sort.key}:${sc.sort.dir}` },
3335
3370
  token: { tape: !!state.tokenTape, social: !!state.tokenSocial, analyst: state.aiOpen !== false },
3336
3371
  popups, widgets,
@@ -3356,6 +3391,12 @@ export function applyKey(state, key, stack) {
3356
3391
  // task-1360: typing a wallet address on the FOLLOWED WALLETS screen captures every key until ⏎ / esc.
3357
3392
  // task-1358 Tier 3: typing a rule on the RULES screen captures every key until ⏎ / esc. Every keystroke re-runs the
3358
3393
  // dry-run ("would match N rows right now"); ⏎ parses + saves (never eval'd), a refusal is the notice.
3394
+ // task-1508: the AUTOTRADE screen's typed lines (a strategy id, a file to import, a field value) and the flatten
3395
+ // confirm capture every key until ⏎ / esc, so q and the screen keys never leak out of a half-typed value.
3396
+ if (state.screen === 'autotrade' && state.autotradeUi && autotradeCapturing(state.autotradeUi)) {
3397
+ autotradeKey(state.autotradeUi, key);
3398
+ return;
3399
+ }
3359
3400
  if (state.screen === 'rules' && state.rulesUi && state.rulesUi.input !== null) {
3360
3401
  const ui = state.rulesUi;
3361
3402
  const cur = state.rulesUi.input;
@@ -3694,8 +3735,8 @@ export function fpsFor(args, intensity) {
3694
3735
  const SYNC_ON = '\x1b[?2026h';
3695
3736
  const SYNC_OFF = '\x1b[?2026l';
3696
3737
  export function interactive(state, io, depth, args, proc = process) {
3697
- // 0.1.5: the analyst engine for new conversations (the user's own claude or codex CLI; never a shipped key).
3698
- state.analystEngine ??= analystEngineFor(args.analystEngine, io.env, state.custom?.config.analyst?.engine);
3738
+ // 0.1.5: the analyst engine for new conversations (the user's own claude or codex CLI, or task-1543 a local Ollama model; never a shipped key).
3739
+ state.analystEngine ??= analystEngineFor(args.analystEngine, io.env, state.custom?.config.analyst?.engine, analystEngineHas(io.env), analystAiChoice(io.env));
3699
3740
  const out = io.stdout;
3700
3741
  const inp = io.stdin;
3701
3742
  const rnd = mulberry32(99);
@@ -4121,7 +4162,7 @@ export function interactive(state, io, depth, args, proc = process) {
4121
4162
  // task-1340: a send in flight shows the neutral scanning sweep (never a verdict); it clears when the ticket settles.
4122
4163
  if (reason === 'ticket') {
4123
4164
  const ph = live.ticket?.ticket.phase;
4124
- if (ph === 'filling' || ph === 'signing') {
4165
+ if (ph === 'filling') {
4125
4166
  if (v.moment?.kind !== 'inflight')
4126
4167
  fireInflight(v, live.ticket?.ticket.symbol ?? '', ticketOrigin());
4127
4168
  }
@@ -4313,7 +4354,7 @@ export function interactive(state, io, depth, args, proc = process) {
4313
4354
  maybeSpeakVoiceAnswer(session); // task-1282: speak a voice-initiated answer when it settles
4314
4355
  scheduleDraw();
4315
4356
  };
4316
- const session = new AnalystSession({ skill: p.skill, mint: p.mint, symbol: p.symbol, screenState: buildScreenState(state), model: p.model, onAction, ...(state.analystEngine === 'codex' ? { engine: 'codex' } : {}) }, onChange);
4357
+ const session = new AnalystSession({ skill: p.skill, mint: p.mint, symbol: p.symbol, screenState: buildScreenState(state), model: p.model, onAction, ...(state.analystEngine && state.analystEngine !== 'claude' ? { engine: state.analystEngine } : {}) }, onChange);
4317
4358
  if (p.question)
4318
4359
  session.ask(p.question, buildScreenState(state));
4319
4360
  else
@@ -4523,7 +4564,7 @@ export function interactive(state, io, depth, args, proc = process) {
4523
4564
  livePrice: (mint) => (live.badgeFor(mint) === 'LIVE' ? live.livePrice(mint) : null),
4524
4565
  feed: (mint) => (live.badgeFor(mint) === 'LIVE' ? 'LIVE' : 'POLL'),
4525
4566
  positions: live.positionSource,
4526
- fills: (mint) => [...(live.livePositions?.records(200, mint) ?? []), ...live.wallet.trades(500).filter((t) => t.mint === mint)],
4567
+ fills: (mint) => live.wallet.trades(500).filter((t) => t.mint === mint),
4527
4568
  solUsd: () => state.sol?.price ?? live.positions.solUsd(),
4528
4569
  imagePath: (v) => popupImagePath(state, v.image),
4529
4570
  priceMode: () => priceModeOf(state), // task-1366
@@ -4531,6 +4572,10 @@ export function interactive(state, io, depth, args, proc = process) {
4531
4572
  setChartAxis: (a) => { say(setChartAxis(state, a), 'info', 3); if (!done)
4532
4573
  draw(); }, // task-1472: the popup's MCAP | PRICE control
4533
4574
  pnlChip: () => pnlChipFor(state, state.agentHud?.clock() ?? Date.now()), // task-1473 r2: the chip on the chart popup
4575
+ markFills: (mint) => live.chartFills.for(mint), // task-1483: BUY/SELL markers (the paper ledger, no fetch)
4576
+ chartMarkers: () => chartMarkersOn(state),
4577
+ setChartMarkers: (on) => { say(setChartMarkers(state, on ? 'on' : 'off'), 'info', 3); if (!done)
4578
+ draw(); }, // task-1483: the popup's MARKERS control
4534
4579
  } : null,
4535
4580
  });
4536
4581
  }
@@ -4794,7 +4839,7 @@ export function interactive(state, io, depth, args, proc = process) {
4794
4839
  else {
4795
4840
  try {
4796
4841
  const s = seedForMint(state, live, tk.mint, tk.symbol);
4797
- live.openTicket({ mint: s.mint, symbol: s.symbol, name: s.name }, tk.side, 'paper');
4842
+ live.openTicket({ mint: s.mint, symbol: s.symbol, name: s.name }, tk.side);
4798
4843
  if (state.screen !== 'ticket')
4799
4844
  stack.push(state.screen);
4800
4845
  state.screen = 'ticket';
@@ -5038,7 +5083,7 @@ export function interactive(state, io, depth, args, proc = process) {
5038
5083
  const res = resolveVoiceToken(d.target);
5039
5084
  if (res.kind === 'ok' && live) {
5040
5085
  const s = seedForMint(state, live, res.mint);
5041
- live.openTicket({ mint: s.mint, symbol: s.symbol, name: s.name }, d.side, 'paper');
5086
+ live.openTicket({ mint: s.mint, symbol: s.symbol, name: s.name }, d.side);
5042
5087
  if (state.screen !== 'ticket')
5043
5088
  stack.push(state.screen);
5044
5089
  state.screen = 'ticket';
@@ -5353,7 +5398,9 @@ export function interactive(state, io, depth, args, proc = process) {
5353
5398
  // task-1388 GO-TO prompt: while the "open any coin" input is open it owns the keyboard. Route the key
5354
5399
  // straight to applyKey (which does the capture / open) and skip the voice, calm, demo and keymap
5355
5400
  // handlers below — otherwise a base58 char like v / m / c would toggle voice or calm instead of typing.
5356
- if (state.goto) {
5401
+ // task-1508: the AUTOTRADE screen's typed lines (an id, a file, a field value) and its flatten confirm own the
5402
+ // keyboard the same way, so v / m / c / n / d / x type instead of toggling voice, calm or a demo event.
5403
+ if (state.goto || (state.screen === 'autotrade' && state.autotradeUi && autotradeCapturing(state.autotradeUi))) {
5357
5404
  const before = state.screen;
5358
5405
  try {
5359
5406
  applyKey(state, key, stack);
@@ -5583,33 +5630,10 @@ export function interactive(state, io, depth, args, proc = process) {
5583
5630
  }
5584
5631
  // else: fall through to the keymap (quit/navigation remain responsive while filling)
5585
5632
  }
5586
- // task-1328 LIVE: while the signer builds the plan, esc cancels; while Phantom has the plan
5587
- // (signing) no key does anything — the signer finishes or times out, and nothing here can
5588
- // confirm, so navigation keys still fall through to the keymap.
5589
- if (state.screen === 'ticket' && live?.ticket && live.ticket.ticket.phase === 'planning') {
5590
- if (key.name === 'esc') {
5591
- live.ticket.cancelPlan();
5592
- draw();
5593
- continue;
5594
- }
5595
- // task-1368: while Jupiter is rate limiting the signer the ticket waits and retries by itself; r retries now.
5596
- if (key.name === 'char' && (key.ch === 'r' || key.ch === 'R') && !key.ctrl && !key.alt) {
5597
- live.ticket.retryPlan();
5598
- draw();
5599
- continue;
5600
- }
5601
- }
5602
- if (state.screen === 'ticket' && live?.ticket && live.ticket.ticket.phase === 'signing') {
5603
- if (key.name === 'esc') {
5604
- state.pendingToast = 'waiting for Phantom — approve or reject it in the browser';
5605
- draw();
5606
- continue;
5607
- }
5608
- }
5609
5633
  // PAPER trade ticket (task-1310): the ticket captures every key (digits/y/esc/tab/…) so the
5610
5634
  // confirm gate can never be satisfied by a stray key, and `q`/`1`-`4` cannot quit or navigate
5611
5635
  // out from under a half-typed order. `filling` is handled above so the user is never trapped.
5612
- const ticketActive = state.screen === 'ticket' && live?.ticket && live.ticket.ticket.phase !== 'filled' && live.ticket.ticket.phase !== 'error' && live.ticket.ticket.phase !== 'refused' && live.ticket.ticket.phase !== 'filling' && live.ticket.ticket.phase !== 'planning' && live.ticket.ticket.phase !== 'signing';
5636
+ const ticketActive = state.screen === 'ticket' && live?.ticket && live.ticket.ticket.phase !== 'filled' && live.ticket.ticket.phase !== 'error' && live.ticket.ticket.phase !== 'filling';
5613
5637
  if (ticketActive && live?.ticket) {
5614
5638
  let eff;
5615
5639
  try {
@@ -5715,79 +5739,6 @@ export function interactive(state, io, depth, args, proc = process) {
5715
5739
  // 'keypress'}) — which the engine takes ONLY from a real keypress, journals, and auto-expires in 24h;
5716
5740
  // restoreGate() puts it back. There is no path here for a pipe/agent (TTY-only loop, no way to raise
5717
5741
  // the modal except a person pressing G), and signer caps/allow-lists/panic are unaffected either way.
5718
- // task-1363 — the FIRE PATH modal: paper ⇄ LIVE (devnet, the capped fast wallet). Same captured-keypress
5719
- // discipline as arm/gate: enter/y switches, esc cancels, every other key is swallowed. Going LIVE needs
5720
- // the run's signer process (never started in replay/offline/--no-live), the signer on DEVNET with a fast
5721
- // wallet loaded (checked by LivePlanExecutor.ready), and no armed/filled plans; PlanService.useLive
5722
- // takes ONLY a keypress gesture and journals the switch. Nothing here signs: fills still go through the
5723
- // signer's own policy, simulation and caps, and the fast wallet never signs for the main wallet.
5724
- if (state.screen === 'plans' && state.plansUi?.liveModal) {
5725
- const ui = state.plansUi;
5726
- if (key.name === 'esc') {
5727
- ui.liveModal = false;
5728
- ui.notice = 'fire path unchanged';
5729
- draw();
5730
- continue;
5731
- }
5732
- const confirmKey = key.name === 'enter' || (key.name === 'char' && !key.ctrl && !key.alt && (key.ch === 'y' || key.ch === 'Y'));
5733
- if (confirmKey) {
5734
- const svc = state.plansSvc;
5735
- ui.liveModal = false;
5736
- if (!svc) {
5737
- ui.notice = 'no plan service';
5738
- draw();
5739
- continue;
5740
- }
5741
- if (svc.mode() === 'live') {
5742
- try {
5743
- svc.usePaper();
5744
- ui.notice = 'fire path: PAPER — fills are simulated again';
5745
- }
5746
- catch (e) {
5747
- ui.notice = `switch refused: ${describeError(e)}`;
5748
- }
5749
- draw();
5750
- continue;
5751
- }
5752
- const port = live?.ensureSigner() ?? null;
5753
- if (!port) {
5754
- ui.notice = 'LIVE fills need the signer process: start dexterm live (not replay/offline/--no-live)';
5755
- draw();
5756
- continue;
5757
- }
5758
- if (svc.engine.openPlans().length) {
5759
- ui.notice = `switch refused: cancel or finish ${svc.engine.openPlans().length} open plan(s) first`;
5760
- draw();
5761
- continue;
5762
- }
5763
- const ex = new LivePlanExecutor({ signer: port, clock: { now: () => Date.now(), sleep: (ms) => new Promise((r) => setTimeout(r, ms)) }, solUsd: () => state.sol?.price ?? live?.positions.solUsd() ?? null, onChange: () => { if (!done) {
5764
- plansTick();
5765
- draw();
5766
- } }, log: (l) => args.verbose && io.stderr.write(` plans-live ${l}\n`) });
5767
- ui.notice = 'checking the signer and the fast wallet…';
5768
- void ex.ready().then((r) => {
5769
- if (done)
5770
- return;
5771
- if (!r.ok) {
5772
- ui.notice = `LIVE refused: ${r.why}`;
5773
- draw();
5774
- return;
5775
- }
5776
- try {
5777
- svc.useLive(ex, { gesture: 'keypress' });
5778
- ui.notice = `fire path: ${ex.describe(r.fastWallet)} · balance ${r.balanceLamports === null ? '?' : (Number(r.balanceLamports) / 1e9).toFixed(4)} SOL · L again for paper`;
5779
- }
5780
- catch (e) {
5781
- ui.notice = `switch refused: ${describeError(e)}`;
5782
- }
5783
- draw();
5784
- });
5785
- draw();
5786
- continue;
5787
- }
5788
- draw();
5789
- continue;
5790
- }
5791
5742
  if (state.screen === 'plans' && state.plansUi?.riskModal) {
5792
5743
  const ui = state.plansUi;
5793
5744
  if (key.name === 'esc') {
@@ -5844,7 +5795,7 @@ export function interactive(state, io, depth, args, proc = process) {
5844
5795
  draw();
5845
5796
  continue;
5846
5797
  }
5847
- // 0.1.5: `/engine [auto|claude|codex]` switches the analyst CLI for new conversations; never sent to a model.
5798
+ // 0.1.5: `/engine [auto|claude|codex|ollama]` switches the analyst engine for new conversations; never sent to a model.
5848
5799
  const en0 = q.startsWith('/') ? parseEngineCommand(q) : null;
5849
5800
  if (en0) {
5850
5801
  state.aiInput = '';
@@ -5852,6 +5803,14 @@ export function interactive(state, io, depth, args, proc = process) {
5852
5803
  draw();
5853
5804
  continue;
5854
5805
  }
5806
+ // task-1537: `/display mcap|price` (also `/basis …`, `/set basis …`) sets the price basis; never sent to a model.
5807
+ const dc0 = q.startsWith('/') ? parseDisplayCommand(q) : null;
5808
+ if (dc0) {
5809
+ state.aiInput = '';
5810
+ say('error' in dc0 ? dc0.error : 'mode' in dc0 ? setPriceMode(state, dc0.mode, '/display') : priceModeNotice(state), 'error' in dc0 ? 'warn' : 'info', 6);
5811
+ draw();
5812
+ continue;
5813
+ }
5855
5814
  // task-1324: `/scan …` in the chat input is a command, never sent to claude.
5856
5815
  const sc0 = q.startsWith('/') ? parseScanCommand(q) : null;
5857
5816
  if (sc0) {
@@ -6012,7 +5971,8 @@ export function interactive(state, io, depth, args, proc = process) {
6012
5971
  const sc = state.screener;
6013
5972
  const editing = state.screen === 'screener' && sc.mode !== 'table';
6014
5973
  const plain = key.name === 'char' && !key.ctrl && !key.alt && key.ch !== undefined;
6015
- if (!editing && plain && key.ch === 'c') {
5974
+ // task-1508: on the AUTOTRADE screen `c` marks strategies to compare (calm stays reachable from any other screen).
5975
+ if (!editing && plain && key.ch === 'c' && state.screen !== 'autotrade') {
6016
5976
  // calm toggle: off ↔ the last active tier
6017
5977
  if (isCalm(v)) {
6018
5978
  v.intensity = lastIntensity;
@@ -6026,7 +5986,7 @@ export function interactive(state, io, depth, args, proc = process) {
6026
5986
  flusher.invalidate();
6027
5987
  continue;
6028
5988
  }
6029
- if (!editing && plain && sc.source === 'fixtures' && Object.hasOwn(DEMO_KEYS, key.ch)) {
5989
+ if (!editing && plain && sc.source === 'fixtures' && state.screen !== 'autotrade' && Object.hasOwn(DEMO_KEYS, key.ch)) {
6030
5990
  const kind = DEMO_KEYS[key.ch];
6031
5991
  try {
6032
5992
  const text = demoEvent(state, kind, rnd);
@@ -6095,7 +6055,7 @@ export function interactive(state, io, depth, args, proc = process) {
6095
6055
  if (state.screen === 'plans')
6096
6056
  syncPlansPopup(); // arm / cancel / tick status changes reach the popup
6097
6057
  syncWidgets(); // task-1358 Tier 5: open pack widgets follow the terminal (throttled per widget + topic)
6098
- // task-1328: `/wallet …` — performed here because link opens a browser and probe opens a LIVE ticket.
6058
+ // task-1328: `/wallet …` — performed here because link opens a browser.
6099
6059
  if (state.pendingWallet) {
6100
6060
  const wc = state.pendingWallet;
6101
6061
  state.pendingWallet = null;
@@ -6107,12 +6067,6 @@ export function interactive(state, io, depth, args, proc = process) {
6107
6067
  void live.walletCommand(wc).then((line) => {
6108
6068
  if (done)
6109
6069
  return;
6110
- if (wc === 'probe' && live.ticket && live.ticket.ticket.mode === 'live') {
6111
- if (state.screen !== 'ticket') {
6112
- stack.push(state.screen);
6113
- state.screen = 'ticket';
6114
- }
6115
- }
6116
6070
  say(line, wc === 'panic' ? 'warn' : 'info', 8);
6117
6071
  scheduleDraw();
6118
6072
  }).catch((e) => { say(`wallet ${wc} failed: ${describeError(e)}`, 'warn', 6); scheduleDraw(); });
@@ -6215,6 +6169,9 @@ export function interactive(state, io, depth, args, proc = process) {
6215
6169
  say(state.updateNotice, 'info', 8);
6216
6170
  state.updateNotice = null;
6217
6171
  }
6172
+ // task-1508: the autotrade screen re-reads the paper runner's status file every 2 s while it is open.
6173
+ if (state.screen === 'autotrade' && state.autotradeUi)
6174
+ autotradeTick(state.autotradeUi);
6218
6175
  if (feed) {
6219
6176
  if (!state.paused && Date.now() - lastRefresh >= LIVE_REFRESH_MS)
6220
6177
  refresh();