dexterm-cli 0.1.2 → 0.1.4
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.
- package/dist/packages/agents/src/bus.js +37 -1
- package/dist/packages/agents/src/follow.js +105 -0
- package/dist/packages/agents/src/index.js +5 -0
- package/dist/packages/agents/src/spectator-server.js +153 -0
- package/dist/packages/agents/src/spectator.js +212 -0
- package/dist/packages/cli/assets/claude/analyst-system.md +7 -1
- package/dist/packages/cli/src/agent-cmd.js +71 -7
- package/dist/packages/cli/src/agent-spectator.js +37 -0
- package/dist/packages/cli/src/alerts/format.js +25 -0
- package/dist/packages/cli/src/app-help-gen.js +365 -0
- package/dist/packages/cli/src/docs-kb-gen.js +228 -0
- package/dist/packages/cli/src/doctor.js +28 -12
- package/dist/packages/cli/src/follow-cmd.js +107 -1
- package/dist/packages/cli/src/help-cmd.js +48 -0
- package/dist/packages/cli/src/index.js +1 -1
- package/dist/packages/cli/src/kb-skill-cmd.js +3 -0
- package/dist/packages/cli/src/larp-detected.js +209 -0
- package/dist/packages/cli/src/live/index.js +64 -3
- package/dist/packages/cli/src/live/screens/token-live.js +24 -0
- package/dist/packages/cli/src/live/signals.js +109 -0
- package/dist/packages/cli/src/live/token.js +38 -1
- package/dist/packages/cli/src/live/trade.js +24 -1
- package/dist/packages/cli/src/main.js +495 -11
- package/dist/packages/cli/src/mcp-cmd.js +2 -7
- package/dist/packages/cli/src/mcp-follow.js +225 -0
- package/dist/packages/cli/src/pnl-app.js +221 -0
- package/dist/packages/cli/src/pnl-card.js +615 -0
- package/dist/packages/cli/src/record-cmd.js +455 -0
- package/dist/packages/cli/src/rules.js +50 -2
- package/dist/packages/cli/src/screens/follows.js +91 -2
- package/dist/packages/cli/src/screens/overlay.js +3 -0
- package/dist/packages/cli/src/screens/positions.js +1 -1
- package/dist/packages/cli/src/screens/rules.js +1 -1
- package/dist/packages/cli/src/spectate-cmd.js +115 -0
- package/dist/packages/cli/src/state.js +1 -1
- package/dist/packages/cli/src/version.js +1 -1
- package/dist/packages/cli/src/voice-cmd.js +86 -11
- package/dist/packages/cli/src/voice-hud.js +1 -1
- package/dist/packages/cli/src/x-cmd.js +11 -5
- package/dist/packages/custom/src/rules.js +2 -1
- package/dist/packages/data/src/analyst-actions.js +130 -3
- package/dist/packages/data/src/fast-lane.js +16 -2
- package/dist/packages/data/src/index.js +1 -0
- package/dist/packages/data/src/intel/live-signals.js +185 -0
- package/dist/packages/data/src/trade/copy/index.js +1 -1
- package/dist/packages/data/src/trade/copy/stats.js +36 -2
- package/dist/packages/data/src/trade/copy/tracker.js +3 -2
- package/dist/packages/mcp/assets/kb/app-help/app-help-command.md +35 -0
- package/dist/packages/mcp/assets/kb/app-help/ask-the-analyst.md +43 -0
- package/dist/packages/mcp/assets/kb/app-help/backtest.md +33 -0
- package/dist/packages/mcp/assets/kb/app-help/bubble-map.md +35 -0
- package/dist/packages/mcp/assets/kb/app-help/calm-mode.md +31 -0
- package/dist/packages/mcp/assets/kb/app-help/change-voice-speed.md +31 -0
- package/dist/packages/mcp/assets/kb/app-help/change-voice.md +30 -0
- package/dist/packages/mcp/assets/kb/app-help/chart-popup-selftest.md +32 -0
- package/dist/packages/mcp/assets/kb/app-help/coin-images.md +29 -0
- package/dist/packages/mcp/assets/kb/app-help/configure-rpc-key.md +28 -0
- package/dist/packages/mcp/assets/kb/app-help/connect-an-agent-registry.md +35 -0
- package/dist/packages/mcp/assets/kb/app-help/connect-an-agent.md +39 -0
- package/dist/packages/mcp/assets/kb/app-help/custom-keymap.md +32 -0
- package/dist/packages/mcp/assets/kb/app-help/edit-kb-or-skill.md +36 -0
- package/dist/packages/mcp/assets/kb/app-help/edit-rules.md +34 -0
- package/dist/packages/mcp/assets/kb/app-help/export-a-view.md +36 -0
- package/dist/packages/mcp/assets/kb/app-help/follow-a-wallet.md +33 -0
- package/dist/packages/mcp/assets/kb/app-help/getting-started.md +37 -0
- package/dist/packages/mcp/assets/kb/app-help/kol-lists.md +34 -0
- package/dist/packages/mcp/assets/kb/app-help/larp-check.md +36 -0
- package/dist/packages/mcp/assets/kb/app-help/link-a-wallet.md +41 -0
- package/dist/packages/mcp/assets/kb/app-help/login-with-x.md +33 -0
- package/dist/packages/mcp/assets/kb/app-help/narrative-board.md +32 -0
- package/dist/packages/mcp/assets/kb/app-help/open-a-coin.md +43 -0
- package/dist/packages/mcp/assets/kb/app-help/open-chart-popup.md +33 -0
- package/dist/packages/mcp/assets/kb/app-help/order-plans.md +33 -0
- package/dist/packages/mcp/assets/kb/app-help/paper-trade.md +36 -0
- package/dist/packages/mcp/assets/kb/app-help/price-or-marketcap.md +38 -0
- package/dist/packages/mcp/assets/kb/app-help/record-headless.md +34 -0
- package/dist/packages/mcp/assets/kb/app-help/recorder-history.md +39 -0
- package/dist/packages/mcp/assets/kb/app-help/rug-check.md +39 -0
- package/dist/packages/mcp/assets/kb/app-help/run-doctor.md +32 -0
- package/dist/packages/mcp/assets/kb/app-help/scan-mode.md +37 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-analyst.md +181 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-kol.md +190 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-newpairs.md +181 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-positions.md +180 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-screener.md +199 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-token.md +240 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-trending.md +192 -0
- package/dist/packages/mcp/assets/kb/app-help/screen-watchlist.md +181 -0
- package/dist/packages/mcp/assets/kb/app-help/set-a-filter.md +36 -0
- package/dist/packages/mcp/assets/kb/app-help/setup-neural-voice.md +36 -0
- package/dist/packages/mcp/assets/kb/app-help/share-packs.md +34 -0
- package/dist/packages/mcp/assets/kb/app-help/sort-the-screener.md +34 -0
- package/dist/packages/mcp/assets/kb/app-help/spectate-an-agent.md +39 -0
- package/dist/packages/mcp/assets/kb/app-help/standing-mandates.md +33 -0
- package/dist/packages/mcp/assets/kb/app-help/switch-theme.md +42 -0
- package/dist/packages/mcp/assets/kb/app-help/turn-on-alerts.md +37 -0
- package/dist/packages/mcp/assets/kb/app-help/update-dexterm.md +30 -0
- package/dist/packages/mcp/assets/kb/app-help/use-presets.md +42 -0
- package/dist/packages/mcp/assets/kb/app-help/use-voice-mode.md +36 -0
- package/dist/packages/mcp/assets/kb/app-help/watch-a-coin.md +34 -0
- package/dist/packages/mcp/assets/kb/app-help/watch-headless.md +35 -0
- package/dist/packages/mcp/assets/kb/app-help/what-dexterm-shows-agents.md +31 -0
- package/dist/packages/mcp/assets/kb/app-help/workspaces.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-agents-capability-tiers-enforced-server-side.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-agents-install-in-one-command.md +49 -0
- package/dist/packages/mcp/assets/kb/docs/docs-agents-per-agent-mandates.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-agents-recipes.md +43 -0
- package/dist/packages/mcp/assets/kb/docs/docs-agents-red-team-coverage.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-agents-two-ways-to-connect.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-agents-untrusted-by-default.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-agents-watch-an-agent-work-spectator-view.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-agents.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-answer-style.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-if-claude-is-missing.md +25 -0
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-it-knows-the-app.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-mcp-tools.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-plain-language-filters.md +25 -0
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-skills.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-starting-a-run.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-it-can-change-three-tiers.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-it-sees.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-analyst-what-the-analyst-can-never-do.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-analyst.md +23 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-1-start-dexterm.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-2-open-a-real-coin-by-contract-address.md +43 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-3-read-the-safety-panel.md +40 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-4-read-the-holder-panel.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-5-optionally-ask-the-analyst.md +39 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-6-export-or-share-what-you-see.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-7-quit-safely.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-before-any-experimental-trading.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-check-the-installation-with-doctor.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-choose-a-setup-path.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-common-mistakes-and-recovery.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-copy-and-paste-without-surprises.md +39 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-if-data-says-unavailable-stale-or-rate-limited.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-if-dexterm-says-command-not-found.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-if-optional-help-is-unavailable.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-if-paste-does-nothing-or-pastes-twice.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-if-the-screen-looks-broken.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-if-you-typed-the-wrong-thing.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-install-node-js-first.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-linux.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-macos-homebrew.md +46 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-macos-or-linux-npm.md +39 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-macos.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-open-the-terminal.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-reopen-dexterm-later.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-ten-minute-first-win.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-terminal-shell-prompt-command-output-and-path.md +40 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-where-to-go-next.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-windows-expectations.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-windows-powershell-experimental.md +39 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners-windows.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-beginners.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-cli-dexterm-help.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-cli-filter-grammar.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-cli-flags.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-cli-invocations.md +43 -0
- package/dist/packages/mcp/assets/kb/docs/docs-cli-presets.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-cli-scan-commands.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-cli-sort-keys.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-cli-subcommands.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-cli.md +25 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-environment-variables.md +45 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-files-on-disk.md +41 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-headless-recorder-dexterm-record.md +44 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-history-recorder.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-packs.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-price-display-mode.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-push-alerts-and-dexterm-watch.md +45 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-rules-engine.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-the-analyst-s-launch.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-the-optional-free-rpc-key.md +42 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-themes-and-keymaps.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-themes-and-look.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-widgets.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-workspaces.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration.md +25 -0
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-1-follow-a-wallet-see-what-it-does.md +51 -0
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-2-the-one-key-mirror-ticket-a-proposal.md +56 -0
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-stage-3-capped-auto-mirror-default-off-less.md +47 -0
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading-what-is-proven-what-is-not.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-copy-trading.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-data-sources-cache.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-data-sources-health-registry.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-data-sources-rate-limits-and-pacing.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-data-sources-request-budget-meter.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-data-sources-sources.md +47 -0
- package/dist/packages/mcp/assets/kb/docs/docs-data-sources-the-helius-credit-meter.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-data-sources.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-dexterm-doctor.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-first-run.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-install.md +71 -0
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-next.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-requirements.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-running-the-tests.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-supported-platforms.md +44 -0
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started-updating.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-getting-started.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-keyboard-global-keys.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-keyboard-keys-handled-by-the-loop.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-keyboard-per-screen-keys.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-keyboard-what-shows.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-keyboard.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-larp-measured-accuracy.md +55 -0
- package/dist/packages/mcp/assets/kb/docs/docs-larp-open-link-keys.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-larp-paste-a-coin-on-the-web.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-larp-the-verdict.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-larp-timestamped-follow-up-drafts.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-larp-what-it-looks-at.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-larp.md +24 -0
- package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-degraded-states-you-will-see.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-free-vs-paid-x-api-tiers-your-bill-your-limits.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-kol-lists-and-the-per-handle-filter.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-tests.md +25 -0
- package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-what-is-read-and-when.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-what-you-set-up-once.md +40 -0
- package/dist/packages/mcp/assets/kb/docs/docs-login-with-x-where-the-tokens-live.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-login-with-x.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-a-live-ticket.md +42 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-before-you-press-size-vs-caps.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-devnet-signer-mainnet-token.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-jupiter-rate-limits.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-link-a-wallet.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-live-trading-devnet-first.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-mainnet.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-order-plans-and-standing-mandates.md +44 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-panic.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-pnl-cards.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-live-fire-path-devnet-the-fast-wallet.md +41 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-signer-process.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-ticket.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-the-wallet.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-trade-moments.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-what-the-live-path-keeps.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading-your-first-live-trade.md +41 -0
- package/dist/packages/mcp/assets/kb/docs/docs-paper-trading.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-bubble-map-popup.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-build-the-native-window.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-config-dexterm-popup-json.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-docking-macos.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-keys-in-the-popup.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-market-cap-axis.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-security-model.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-token-and-feeds-popups.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-popup-chart-what-it-shows.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-popup-chart.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-scan-cost-control.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-scan-keys-flag-command.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-scan-safety-four-layers.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-scan-states.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-scan-the-read.md +25 -0
- package/dist/packages/mcp/assets/kb/docs/docs-scan-triggers-and-score.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-scan.md +24 -0
- package/dist/packages/mcp/assets/kb/docs/docs-scorecard-frozen-canary.md +30 -0
- package/dist/packages/mcp/assets/kb/docs/docs-scorecard-frozen-task-1401-evaluation.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-scorecard-latest-skill-care-run.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-scorecard-links-only-larp-baseline-where-it-is-weak.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-scorecard-not-measured.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-scorecard-rug-verdict-rules.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-scorecard-source-and-rebuild.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-scorecard.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-1-screener.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-2-new-pairs.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-3-watchlist.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-4-analyst-tab.md +28 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-bubble-map.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-coin-images.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-live-holder-and-rug-signals.md +51 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-order-plans.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-paper-ticket-and-positions.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-the-look.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-token-screen.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-trending-social-and-kol-lists.md +34 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-1-the-agent-activity-window-follow-mode.md +56 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-2-the-read-only-stream-spectate.md +42 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-3-save-and-replay-record-file-dexterm-spectate.md +41 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-follow-over-mcp-in-the-running-terminal.md +39 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-limits.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-quick-start.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-security-model.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-see-also.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-streaming-it.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-the-three-surfaces.md +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator-what-is-redacted.md +35 -0
- package/dist/packages/mcp/assets/kb/docs/docs-spectator.md +29 -0
- package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-common-questions.md +33 -0
- package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-doctor-lines.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-in-app-messages.md +42 -0
- package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting-reporting-a-problem.md +25 -0
- package/dist/packages/mcp/assets/kb/docs/docs-troubleshooting.md +23 -0
- package/dist/packages/mcp/assets/kb/docs/docs-voice-doctor.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-voice-it-clarifies-it-never-guesses.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-voice-latency.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-voice-linux-and-windows.md +32 -0
- package/dist/packages/mcp/assets/kb/docs/docs-voice-privacy.md +26 -0
- package/dist/packages/mcp/assets/kb/docs/docs-voice-still-unverified.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-voice-the-voice-neural-by-default.md +48 -0
- package/dist/packages/mcp/assets/kb/docs/docs-voice-what-it-can-never-do.md +27 -0
- package/dist/packages/mcp/assets/kb/docs/docs-voice-what-you-can-say.md +38 -0
- package/dist/packages/mcp/assets/kb/docs/docs-voice.md +25 -0
- package/dist/packages/mcp/src/app-help.js +258 -0
- package/dist/packages/mcp/src/firewall.js +2 -2
- package/dist/packages/mcp/src/index.js +5 -1
- package/dist/packages/mcp/src/server.js +129 -4
- package/dist/packages/mcp/src/spectator-sink.js +144 -0
- package/dist/packages/packs/src/index.js +2 -0
- package/dist/packages/packs/src/skill-care.js +206 -0
- package/dist/packages/popup/src/pages/spectator.css +45 -0
- package/dist/packages/popup/src/pages/spectator.html +25 -0
- package/dist/packages/popup/src/pages/spectator.js +74 -0
- package/dist/packages/popup/src/selftest.js +19 -0
- package/dist/packages/popup/src/server.js +7 -1
- package/dist/packages/tui/src/keymap.js +10 -0
- package/dist/packages/voice/src/controller.js +42 -1
- package/dist/packages/voice/src/doctor.js +25 -8
- package/dist/packages/voice/src/engines.js +8 -1
- package/dist/packages/voice/src/index.js +3 -1
- package/dist/packages/voice/src/speakable.js +111 -0
- package/dist/packages/voice/src/stt/whisper.js +4 -2
- package/dist/packages/voice/src/tts/piper-setup.js +229 -0
- package/dist/packages/voice/src/tts/piper.js +309 -66
- package/dist/packages/voice/src/tts/say.js +3 -2
- package/dist/packages/voice/src/tts/voices.js +8 -5
- package/package.json +6 -5
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// config; it is never stored in plaintext and never echoed again. The token for `serve`/`connect` auth is
|
|
16
16
|
// read ONLY from the $DEXTERM_AGENT_TOKEN environment variable — never a command-line flag — so it cannot
|
|
17
17
|
// leak through a process list. No agent path can arm, sign, send, raise a cap or touch a wallet/mandate.
|
|
18
|
-
import { AgentRegistry, SqliteAuditLog, MemoryProposalStore, SqliteProposalStore, AgentBus, FixtureExecutor, FixtureFeed, TIERS, isTier, } from '../../agents/src/index.js';
|
|
18
|
+
import { AgentRegistry, SqliteAuditLog, MemoryProposalStore, SqliteProposalStore, AgentBus, FixtureExecutor, FixtureFeed, SpectatorHub, SpectatorServer, SpectatorRecorder, FollowController, TIERS, isTier, } from '../../agents/src/index.js';
|
|
19
19
|
import { connectAgent } from '../../agents/src/sdk.js';
|
|
20
20
|
import { defaultDbPath } from '../../data/src/index.js';
|
|
21
21
|
function flag(rest, name) { const i = rest.indexOf(name); return i >= 0 ? rest[i + 1] : undefined; }
|
|
@@ -28,7 +28,7 @@ const USAGE = `dexterm agent <command>
|
|
|
28
28
|
audit [--agent NAME] [--limit N]
|
|
29
29
|
proposals [--agent NAME] [--status pending|confirmed|dismissed] [--limit N] [--db PATH]
|
|
30
30
|
packs
|
|
31
|
-
serve [--fixtures|--live] [--roster] [--port N] [--db PATH]
|
|
31
|
+
serve [--fixtures|--live] [--roster] [--spectate] [--spectator-popup] [--record FILE] [--port N] [--db PATH]
|
|
32
32
|
connect --name NAME [--port N] [--url ws://127.0.0.1:PORT] [--watch SECONDS] (token via $DEXTERM_AGENT_TOKEN)`;
|
|
33
33
|
export async function runAgent(rest, io) {
|
|
34
34
|
const cmd = rest[0];
|
|
@@ -257,12 +257,36 @@ async function serve(rest, io, registry) {
|
|
|
257
257
|
else {
|
|
258
258
|
executor = new FixtureExecutor();
|
|
259
259
|
}
|
|
260
|
-
|
|
260
|
+
// task-1406: the spectator hub observes every agent action; it feeds follow mode, the read-only stream
|
|
261
|
+
// and the save file. Created whenever any spectator surface is asked for (cheap; off otherwise).
|
|
262
|
+
const wantSpectate = has(rest, '--spectate');
|
|
263
|
+
const wantRecord = flag(rest, '--record');
|
|
264
|
+
const wantSpectatorPopup = has(rest, '--spectator-popup');
|
|
265
|
+
const hub = (wantSpectate || wantRecord || wantSpectatorPopup) ? new SpectatorHub() : undefined;
|
|
266
|
+
const bus = new AgentBus({ registry, audit, executor, proposals, feed, spectator: hub, port, env: io.env });
|
|
261
267
|
const boundPort = await bus.start();
|
|
262
268
|
feed.start(1500);
|
|
263
269
|
io.stdout.write(`dexterm agent bus listening on ws://127.0.0.1:${boundPort} (${live ? 'live keyless reads' : 'fixtures'})\n`);
|
|
264
270
|
io.stdout.write(`discovery: ${process.env.DEXTERM_HOME ?? '~/.dexterm'}/agent-bus.json · ${registry.list().length} registered agent(s)\n`);
|
|
265
271
|
io.stdout.write('agents connect with their name + $DEXTERM_AGENT_TOKEN. Ctrl-C to stop.\n');
|
|
272
|
+
// --record FILE: save the session (redacted) for demos / bug reports; replay with `dexterm spectate --replay`.
|
|
273
|
+
let recorder = null;
|
|
274
|
+
if (hub && wantRecord) {
|
|
275
|
+
try {
|
|
276
|
+
recorder = new SpectatorRecorder({ path: wantRecord, note: 'dexterm agent serve' }).attach(hub);
|
|
277
|
+
io.stdout.write(`recording the session to ${wantRecord} (redacted).\n`);
|
|
278
|
+
}
|
|
279
|
+
catch (e) {
|
|
280
|
+
io.stderr.write(`could not record: ${e.message}\n`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
// --spectate: start the read-only, loopback, token-gated spectator stream (SPECTATOR.md).
|
|
284
|
+
let stream = null;
|
|
285
|
+
if (hub && wantSpectate) {
|
|
286
|
+
stream = new SpectatorServer({ hub, env: io.env });
|
|
287
|
+
const sp = await stream.start();
|
|
288
|
+
io.stdout.write(`spectator stream (read-only) on ws://127.0.0.1:${sp} · token in ${process.env.DEXTERM_HOME ?? '~/.dexterm'}/spectator.json (0600) · redacts addresses/keys\n`);
|
|
289
|
+
}
|
|
266
290
|
// --roster: open the agent roster popup and keep it live. The popup can only send pause/resume/kick.
|
|
267
291
|
let roster = null;
|
|
268
292
|
if (has(rest, '--roster')) {
|
|
@@ -287,11 +311,51 @@ async function serve(rest, io, registry) {
|
|
|
287
311
|
io.stderr.write(`roster popup unavailable: ${e.message}\n`);
|
|
288
312
|
}
|
|
289
313
|
}
|
|
314
|
+
// --spectator-popup: open the spectator popup ("watch the agent cook"), fed by a follow controller that
|
|
315
|
+
// folds the hub's events into the screen model + activity. It sends only spectator.* (pause/takeover/kill).
|
|
316
|
+
let spectatorPopup = null;
|
|
317
|
+
if (hub && wantSpectatorPopup) {
|
|
318
|
+
try {
|
|
319
|
+
const { createPopupManager } = await import('../../popup/src/index.js');
|
|
320
|
+
const { buildSpectatorVM, applySpectatorEvent } = await import("./agent-spectator.js");
|
|
321
|
+
const { THEMES, DEFAULT_THEME } = await import('../../tui/src/index.js');
|
|
322
|
+
const { popupThemeFrom } = await import("./popup.js");
|
|
323
|
+
const follow = new FollowController({ controls: bus, activityCap: 200 });
|
|
324
|
+
const off = hub.on((ev) => follow.apply(ev));
|
|
325
|
+
const mgr = createPopupManager({ theme: popupThemeFrom(THEMES[DEFAULT_THEME]), env: io.env, onWarn: (m) => io.stderr.write(`spectator popup: ${m}\n`),
|
|
326
|
+
onEvent: (ev) => { if (ev.ev === 'popup.event' && ev.name.startsWith('spectator.'))
|
|
327
|
+
io.stdout.write(`spectator: ${applySpectatorEvent(follow, ev.name, String(ev.payload.agent))}\n`); } });
|
|
328
|
+
const handle = await mgr.open({ kind: 'spectator', title: 'watch the agent cook', data: buildSpectatorVM(follow) });
|
|
329
|
+
const timer = setInterval(() => { try {
|
|
330
|
+
mgr.setData(handle.id, buildSpectatorVM(follow));
|
|
331
|
+
}
|
|
332
|
+
catch { /* popup closed */ } }, 1000);
|
|
333
|
+
timer.unref?.();
|
|
334
|
+
spectatorPopup = { shutdown: () => mgr.shutdown(), timer, off };
|
|
335
|
+
io.stdout.write(`spectator popup open (${mgr.native ? 'native window' : 'browser fallback'}).\n`);
|
|
336
|
+
}
|
|
337
|
+
catch (e) {
|
|
338
|
+
io.stderr.write(`spectator popup unavailable: ${e.message}\n`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
290
341
|
await new Promise((resolve) => {
|
|
291
|
-
const stop = () => {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
342
|
+
const stop = () => {
|
|
343
|
+
if (roster) {
|
|
344
|
+
clearInterval(roster.timer);
|
|
345
|
+
void roster.shutdown();
|
|
346
|
+
}
|
|
347
|
+
if (spectatorPopup) {
|
|
348
|
+
clearInterval(spectatorPopup.timer);
|
|
349
|
+
spectatorPopup.off();
|
|
350
|
+
void spectatorPopup.shutdown();
|
|
351
|
+
}
|
|
352
|
+
recorder?.stop();
|
|
353
|
+
feed.stop();
|
|
354
|
+
audit.close();
|
|
355
|
+
proposals.close();
|
|
356
|
+
dataClose?.();
|
|
357
|
+
void (stream ? stream.close() : Promise.resolve()).then(() => bus.close().then(resolve));
|
|
358
|
+
};
|
|
295
359
|
process.once('SIGINT', stop);
|
|
296
360
|
process.once('SIGTERM', stop);
|
|
297
361
|
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// task-1406 "watch the agent cook": bridge the follow-mode controller to the spectator popup VM, and map
|
|
2
|
+
// the popup's spectator.* UI events onto the controller's user-side controls. Nothing here can arm, sign
|
|
3
|
+
// or raise a cap — the controls are exactly the bus's pause/resume/kick, the same as the roster popup.
|
|
4
|
+
import { redactSpectatorEvent, maskAddress } from '../../agents/src/index.js';
|
|
5
|
+
function activityVM(ev) {
|
|
6
|
+
return {
|
|
7
|
+
seq: ev.seq, t: ev.t, phase: ev.phase, agent: ev.agent, tier: ev.tier ?? null,
|
|
8
|
+
tool: ev.tool ?? null, summary: ev.summary ?? '', ms: ev.ms ?? null, dataAgeSec: ev.dataAgeSec ?? null,
|
|
9
|
+
ok: ev.ok, refusal: ev.refusal ?? null,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/** Build the spectator popup VM from the follow controller. Redacts addresses/keys unless redact:false. */
|
|
13
|
+
export function buildSpectatorVM(follow, opts = {}) {
|
|
14
|
+
const redact = opts.redact ?? true;
|
|
15
|
+
const v = follow.view();
|
|
16
|
+
const s = v.screen;
|
|
17
|
+
const mask = (x) => (x && redact ? maskAddress(x) : x);
|
|
18
|
+
return {
|
|
19
|
+
title: opts.title ?? 'watch the agent cook',
|
|
20
|
+
now: Date.now(),
|
|
21
|
+
redacted: redact,
|
|
22
|
+
control: v.control,
|
|
23
|
+
screen: { screen: s.screen, selectedMint: mask(s.selectedMint), feed: s.feed, filter: s.filter, highlight: mask(s.highlight) },
|
|
24
|
+
agents: v.agents.map((a) => ({ name: a.name, tier: a.tier, status: a.status, lastAt: a.lastAt })),
|
|
25
|
+
activity: v.activity.map((ev) => activityVM(redact ? redactSpectatorEvent(ev) : ev)),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** Apply a validated spectator popup event to the follow controller. Returns a short log line. */
|
|
29
|
+
export function applySpectatorEvent(follow, name, agent) {
|
|
30
|
+
switch (name) {
|
|
31
|
+
case 'spectator.pause': return follow.pause(agent);
|
|
32
|
+
case 'spectator.resume': return follow.resume(agent);
|
|
33
|
+
case 'spectator.takeover': return follow.takeOver(agent);
|
|
34
|
+
case 'spectator.kill': return follow.kill(agent);
|
|
35
|
+
default: return `ignored ${name}`;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -8,6 +8,8 @@ import { firewallText } from '../../../data/src/index.js';
|
|
|
8
8
|
import { sanitizeSpeech } from '../../../voice/src/index.js';
|
|
9
9
|
const TITLE_MAX = 64;
|
|
10
10
|
const BODY_MAX = 180;
|
|
11
|
+
/** Local short address (the copy module's own, inlined so the delivery layer imports no copy runtime). */
|
|
12
|
+
function shortAddr(a) { return a.length <= 10 ? a : `${a.slice(0, 4)}…${a.slice(-4)}`; }
|
|
11
13
|
/** Firewall a string; keep the row's own mint readable, drop URLs, cap length. */
|
|
12
14
|
function fw(s, max, mint) {
|
|
13
15
|
return firewallText(s, { keepMint: mint ?? null, keepUrls: false, max }).text;
|
|
@@ -35,6 +37,29 @@ export function proposalToDelivery(p) {
|
|
|
35
37
|
symbol, mint: p.mint, source: p.source, rule: p.rule, at: p.at,
|
|
36
38
|
};
|
|
37
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* task-1399 — a WALLET TRACKER event (a followed wallet's decoded buy/sell) as a firewalled AlertDelivery, so a
|
|
42
|
+
* follow alert reaches the same push channels (desktop / Telegram / Discord) as a rule alert. The delivery layer
|
|
43
|
+
* stays read-only: this only READS the CopyAlert. `source` is `follow`; `rule` names the followed wallet (short,
|
|
44
|
+
* never a real-person label beyond the user's own words). A buy is `up`, a sell `down`. No performance claim: the
|
|
45
|
+
* body states the fill and carries "not a recommendation".
|
|
46
|
+
*/
|
|
47
|
+
export function copyAlertToDelivery(a) {
|
|
48
|
+
const tr = a.trade;
|
|
49
|
+
const did = tr.side === 'buy' ? 'bought' : 'sold';
|
|
50
|
+
const who = fw(a.name ?? shortAddr(a.wallet), 24) || shortAddr(a.wallet);
|
|
51
|
+
const mintShort = shortAddr(tr.mint);
|
|
52
|
+
const usd = tr.usd !== null ? ` ≈ $${tr.usd.toFixed(0)}` : '';
|
|
53
|
+
const sol = tr.solDelta !== null ? ` (${Math.abs(tr.solDelta).toFixed(3)} SOL)` : '';
|
|
54
|
+
const symbol = mintShort;
|
|
55
|
+
const title = `follow: ${who} ${did}`.slice(0, TITLE_MAX);
|
|
56
|
+
const body = fw(`${who} ${did} ${mintShort}${usd}${sol} · not a recommendation`, BODY_MAX, tr.mint);
|
|
57
|
+
return {
|
|
58
|
+
kind: 'alert', level: tr.side === 'buy' ? 'up' : 'down', title, body,
|
|
59
|
+
spoken: sanitizeSpeech(`${who} ${did} ${mintShort}${usd}`),
|
|
60
|
+
symbol, mint: tr.mint, source: 'follow', rule: shortAddr(a.wallet), at: a.t,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
38
63
|
/** The one-line form a text channel sends (Telegram / Discord / stdout toast). */
|
|
39
64
|
export function deliveryLine(d) {
|
|
40
65
|
return d.title === `${d.symbol} ⚑` ? `${d.title} ${d.body}` : `${d.title} — ${d.body}`;
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
// dexterm — generator for the app-help knowledge base (task-1413).
|
|
2
|
+
//
|
|
3
|
+
// Builds the `@dexterm/mcp` app-help KB entries (assets/kb/app-help/*.md) from the app's OWN authoritative
|
|
4
|
+
// sources so the help text cannot drift from the app:
|
|
5
|
+
// - the keymap (packages/tui/src/keymap.ts: DEFAULT_KEYMAP) → the exact key for a task
|
|
6
|
+
// - the help overlay (packages/cli/src/screens/overlay.ts: helpRows) → per-screen key lists
|
|
7
|
+
// - the CLI (packages/cli/src/main.ts: subcommands + flags) → the exact command
|
|
8
|
+
// - the MCP tool list (packages/mcp/src/server.ts: TOOLS) → the exact tool names
|
|
9
|
+
// The prose (titles, bodies, synonyms, which key/command a topic uses) is a committed TOPICS table below.
|
|
10
|
+
// The STRUCTURE (resolved keys, the tool/command/flag sets) is read from the app at generate time, so a
|
|
11
|
+
// re-run after a keymap/tool change rewrites the entries and the drift test (regenerate-and-diff, plus the
|
|
12
|
+
// bidirectional structural checks) fails until they are regenerated.
|
|
13
|
+
//
|
|
14
|
+
// Deterministic by construction: no clock, no network, no filesystem writes here (the script does that).
|
|
15
|
+
// `GENERATED_DATE` is a constant so regenerate-and-diff is stable; bump it when regenerating deliberately.
|
|
16
|
+
import fs from 'node:fs';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
import { fileURLToPath } from 'node:url';
|
|
19
|
+
import { DEFAULT_KEYMAP } from '../../tui/src/index.js';
|
|
20
|
+
import { helpRows } from "./screens/overlay.js";
|
|
21
|
+
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
/** Repo root: packages/cli/src → up three. */
|
|
23
|
+
const ROOT = path.resolve(HERE, '..', '..', '..');
|
|
24
|
+
/** Stamped into every generated entry; bump on a deliberate regenerate. */
|
|
25
|
+
export const GENERATED_DATE = '2026-10-16';
|
|
26
|
+
// ---------------------------------------------------------------------------------------- app sources
|
|
27
|
+
/** Pretty single-key display, mirroring overlay.ts prettyKey for the handful of keys topics use. */
|
|
28
|
+
function prettyKey(id) {
|
|
29
|
+
const map = { enter: '⏎', escape: 'esc', tab: 'tab', space: 'space', up: '↑', down: '↓', left: '←', right: '→' };
|
|
30
|
+
if (map[id])
|
|
31
|
+
return map[id];
|
|
32
|
+
if (id.startsWith('ctrl+'))
|
|
33
|
+
return '^' + id.slice(5).toUpperCase().replace(/\+/g, ' ');
|
|
34
|
+
if (id.startsWith('alt+'))
|
|
35
|
+
return '⌥' + id.slice(4);
|
|
36
|
+
return id;
|
|
37
|
+
}
|
|
38
|
+
/** The first binding for an action (screen-specific preferred, else global); its canonical keys + display. */
|
|
39
|
+
function keyFor(action, screen) {
|
|
40
|
+
const bindings = DEFAULT_KEYMAP.bindings;
|
|
41
|
+
const pick = bindings.find((b) => b.action === action && (screen ? b.screen === screen : false))
|
|
42
|
+
?? bindings.find((b) => b.action === action && !b.screen)
|
|
43
|
+
?? bindings.find((b) => b.action === action);
|
|
44
|
+
if (!pick)
|
|
45
|
+
return { keys: [], display: '' };
|
|
46
|
+
return { keys: [...pick.keys], display: [...new Set(pick.keys.map(prettyKey))].join(' / ') };
|
|
47
|
+
}
|
|
48
|
+
/** Labeled actions + their canonical keys for a screen (every key that screen shows), from the keymap. */
|
|
49
|
+
function screenActions(screen) {
|
|
50
|
+
const actions = [];
|
|
51
|
+
const keys = new Set();
|
|
52
|
+
for (const b of DEFAULT_KEYMAP.forScreen(screen)) {
|
|
53
|
+
if (!b.label)
|
|
54
|
+
continue;
|
|
55
|
+
if (!actions.includes(b.action))
|
|
56
|
+
actions.push(b.action);
|
|
57
|
+
for (const k of b.keys)
|
|
58
|
+
keys.add(k);
|
|
59
|
+
}
|
|
60
|
+
return { actions, keys: [...keys] };
|
|
61
|
+
}
|
|
62
|
+
function read(rel) { return fs.readFileSync(path.join(ROOT, rel), 'utf8'); }
|
|
63
|
+
/** Every `dexterm <subcommand>` main() dispatches (same regex the site's facts.mjs uses). */
|
|
64
|
+
export function appSubcommands() {
|
|
65
|
+
const src = read('packages/cli/src/main.ts');
|
|
66
|
+
return [...new Set([...src.matchAll(/argv\[0\] === '([a-z]+)'/g)].map((m) => m[1]))];
|
|
67
|
+
}
|
|
68
|
+
/** Every `--flag` parseArgs() accepts (main flags; scan `--scan-*` are a separate table the site adds). */
|
|
69
|
+
export function appFlags() {
|
|
70
|
+
const src = read('packages/cli/src/main.ts');
|
|
71
|
+
const body = src.slice(src.indexOf('export function parseArgs'), src.indexOf('export function intensityFor'));
|
|
72
|
+
const flags = [];
|
|
73
|
+
for (const m of body.matchAll(/k === '(--?[a-z-]+)'/g))
|
|
74
|
+
if (!flags.includes(m[1]))
|
|
75
|
+
flags.push(m[1]);
|
|
76
|
+
return flags;
|
|
77
|
+
}
|
|
78
|
+
/** Every MCP tool the server registers (same regex the site's facts.mjs uses). */
|
|
79
|
+
export function appTools() {
|
|
80
|
+
const src = read('packages/mcp/src/server.ts');
|
|
81
|
+
return [...src.matchAll(/\{ name: '([a-z_]+)', description: (?:'((?:[^'\\]|\\.)*)'|"((?:[^"\\]|\\.)*)")/g)].map((m) => m[1]);
|
|
82
|
+
}
|
|
83
|
+
// ---------------------------------------------------------------------------------------- the content
|
|
84
|
+
/** Screens that get a "what it shows + its keys" entry. */
|
|
85
|
+
const SCREENS = [
|
|
86
|
+
{ screen: 'screener', title: 'The screener screen', feature: 'screener', tools: ['screener_query'],
|
|
87
|
+
blurb: 'The live table of Solana tokens (DexScreener + Jupiter, refreshed every ~10 s): price, 5m/1h/6h/24h change, volume, liquidity, market cap, age, holders, organic score, flags and a sparkline, with a p(rug) column. Cells whose source is down show a dash and the caption says why — never a fabricated number.' },
|
|
88
|
+
{ screen: 'token', title: 'The token screen', feature: 'token-intel', tools: ['get_dossier', 'get_token', 'get_holders', 'get_trades', 'get_candles', 'get_dev_history', 'rugcheck'],
|
|
89
|
+
blurb: 'One coin in depth: market stats, a candle chart, the trades tape, mint/freeze authority, top holders with their class (pool/dev/linked/…), RugCheck + GoPlus, snipers/bundled from the wallet-intel scan, and the ANALYST panel. Open it with ⏎ on any live row or `--mint <CA|link>`.' },
|
|
90
|
+
{ screen: 'newpairs', title: 'The new pairs screen', feature: 'new-pairs', tools: ['screener_query'],
|
|
91
|
+
blurb: 'A live feed of just-launched tokens (PumpPortal new-token + migration push, plus Jupiter/RugCheck/DexScreener polls). Bonding progress is a create-time estimate and says so; SNIPE/BUNDLE columns fill in from the wallet-intel scan of the row the cursor rests on (Helius-keyed).' },
|
|
92
|
+
{ screen: 'watchlist', title: 'The watchlist screen', feature: 'watchlist', tools: [],
|
|
93
|
+
blurb: 'The coins you watch, persisted in SQLite, priced live every ~10 s with a LIVE/POLL badge. Add with `w` on any coin; remove with `d`.' },
|
|
94
|
+
{ screen: 'analyst', title: 'The analyst screen', feature: 'analyst', tools: ['get_dossier', 'kb_search', 'app_help'],
|
|
95
|
+
blurb: 'Your own `claude` CLI as the dexterm analyst: it reads the read-only dexterm MCP tools, grounds every fact in a tool result, and answers in a few terse lines. ⏎ on a coin runs a full dossier; `a` runs a rug-check. Facts never come from model memory; token text passes the prompt firewall.' },
|
|
96
|
+
{ screen: 'positions', title: 'The paper positions screen', feature: 'positions', tools: [],
|
|
97
|
+
blurb: 'Your simulated (PAPER) positions and their P&L. dexterm holds no keys and signs nothing; paper trading is a simulation for learning the flow.' },
|
|
98
|
+
{ screen: 'trending', title: 'The trending screen', feature: 'trending-board', tools: ['narrative_board'],
|
|
99
|
+
blurb: 'What is trending in the space — narratives and movers aggregated from the feed.' },
|
|
100
|
+
{ screen: 'kol', title: 'The KOL lists screen', feature: 'kol-lists', tools: [],
|
|
101
|
+
blurb: 'Key-opinion-leader wallet lists and their activity (official/authorized sources only).' },
|
|
102
|
+
];
|
|
103
|
+
/** The screen keys that get an overview entry (for the drift test's forward key-coverage check). */
|
|
104
|
+
export const SCREENS_DOCUMENTED = SCREENS.map((s) => s.screen);
|
|
105
|
+
/**
|
|
106
|
+
* Flags that are deliberately NOT given a "how do I" entry (task-1413 r2): internal / render / replay /
|
|
107
|
+
* debug knobs a normal user never reaches for, so an absent answer for them is correct, not a drift bug.
|
|
108
|
+
* The forward-flag drift test requires every OTHER accepted flag to be named by some app-help entry.
|
|
109
|
+
* Keep this list exhaustive and honest — a user-facing flag must earn a topic, not hide here.
|
|
110
|
+
*/
|
|
111
|
+
export const INTERNAL_FLAGS = new Set([
|
|
112
|
+
'--max-requests', '--max-credits', // request/credit budget limiters (data layer tuning)
|
|
113
|
+
'--listen', '--db', // bus listen address + sqlite path overrides
|
|
114
|
+
'--verbose', // extra diagnostic logging
|
|
115
|
+
'--width', '--height', '--html', '--selected', '--title', // screenshot/export renderer internals
|
|
116
|
+
'--seed', '--tz', '--color', // determinism + rendering overrides for tests/screenshots
|
|
117
|
+
'--fps', '--frame', '--t', '--event', '--ai', '--mood', '--activity', '--no-boot', // demo/replay/render scripting
|
|
118
|
+
'--fit', '--font', '--ascii-rain', // popup / render tuning
|
|
119
|
+
'--plain', '--ansi', // output-format overrides (CI / piping)
|
|
120
|
+
]);
|
|
121
|
+
const TOPICS = [
|
|
122
|
+
{ id: 'getting-started', title: 'Get started with dexterm', screen: 'all', tags: ['start', 'install', 'first', 'run', 'setup', 'begin'],
|
|
123
|
+
commands: ['doctor'], flags: ['--fixtures', '--calm', '--version', '-h', '--help'], feature: 'doctor',
|
|
124
|
+
body: 'Run `dexterm doctor` first: it checks Node, terminal colour, the claude CLI, each data source, the Helius key and a writable data dir, and prints the exact fix for anything not passing. Then run `dexterm` — the first launch shows a one-screen welcome; press any key to enter the live screener. Press `?` on any screen for that screen\'s full key list. Try `--fixtures` for an offline demo. `--version` prints the version; `--help` (or `-h`) prints usage.' },
|
|
125
|
+
{ id: 'open-a-coin', title: 'Open / paste a coin (contract address or link)', screen: 'all', tags: ['paste', 'open', 'coin', 'contract', 'address', 'ca', 'link', 'pump', 'dexscreener', 'solscan', 'go', 'goto'],
|
|
126
|
+
actions: [{ action: 'goto.open', screen: 'screener' }], commands: [], flags: ['--mint'], tools: ['open_token'], feature: 'go-to-coin',
|
|
127
|
+
body: 'Press `g` to open the go-to prompt and paste a contract address — or a dexscreener / pump.fun / solscan / birdeye link — then ⏎ to open that coin\'s live token screen, read-only. A CA or link also works in the analyst chat and the screener `/` line. One-shot: `--mint <CA|link>`. Nothing here trades.' },
|
|
128
|
+
{ id: 'export-a-view', title: 'Export or share the current view', screen: 'all', tags: ['export', 'share', 'screenshot', 'png', 'report', 'save', 'image'],
|
|
129
|
+
actions: [{ action: 'export.view' }], flags: ['--export'], feature: 'export-share',
|
|
130
|
+
body: 'Press `E` on any screen to write the current view to `~/.dexterm/exports/` as a PNG and a plain-text report (the toast names the file). The PNG is rendered calm with a data-source label (LIVE / SAMPLE / REPLAY) and the age of every panel. Nothing is uploaded or posted. One-shot: `--export [DIR]`.' },
|
|
131
|
+
{ id: 'change-voice-speed', title: 'Change the voice speed (rate)', screen: 'all', tags: ['voice', 'speed', 'rate', 'faster', 'slower', 'wpm'],
|
|
132
|
+
commands: ['voice'], feature: 'jarvis',
|
|
133
|
+
body: 'Voice speed is `voice.rate` in `~/.dexterm/config.json` (words per minute, 80–400). Edit it there and restart. Voice settings are yours alone: the analyst/agent CANNOT change them (the voice path is denied to models) — only you set them. `dexterm voice status` shows the current voice engines.' },
|
|
134
|
+
{ id: 'change-voice', title: 'Change which voice speaks', screen: 'all', tags: ['voice', 'choose', 'speak', 'say', 'name'],
|
|
135
|
+
commands: ['voice'], feature: 'jarvis',
|
|
136
|
+
body: 'The default speaking voice is the neural Piper voice once it is set up (`dexterm voice setup`), with the built-in macOS `say` as the fallback. Press `,` to cycle the `say` voice while running, or set `voice.name` in `~/.dexterm/config.json` (that chooses the `say` fallback voice; Piper has one fixed voice). Nothing is sent to a paid API. Voice is user-only: the analyst/agent cannot change it (the voice path is denied to models) — only the keyboard or your config sets it.' },
|
|
137
|
+
{ id: 'setup-neural-voice', title: 'Set up the neural voice (make it sound better)', screen: 'all', tags: ['voice', 'neural', 'piper', 'better', 'quality', 'natural', 'realistic', 'nicer', 'improve', 'install', 'alan'],
|
|
138
|
+
commands: ['voice'], feature: 'jarvis',
|
|
139
|
+
body: 'dexterm can speak with a neural British voice (Piper, en_GB-alan-medium) that sounds markedly more natural than the built-in system voices. Run `dexterm voice setup` to install it: it asks first, builds a private local Python runtime and downloads a ~61 MB checksum-verified voice from the official source, then runs fully offline with no key. Once installed it becomes the DEFAULT speaking voice automatically (the system voice stays as the fallback); `dexterm voice uninstall` removes it. It needs Python 3.9+ — `dexterm doctor` prints the exact fix if it is missing, and shows which engine is speaking.' },
|
|
140
|
+
{ id: 'use-voice-mode', title: 'Use voice mode (Jarvis, push-to-talk)', screen: 'all', tags: ['voice', 'jarvis', 'talk', 'mic', 'microphone', 'push', 'listen'],
|
|
141
|
+
actions: [{ action: 'voice.talk' }], flags: ['--sound'], feature: 'jarvis',
|
|
142
|
+
body: 'Press `v` to start push-to-talk: speak, press `v` again to send (a red ● LISTENING badge shows while the mic is live; it also auto-closes after 15 s). Instant commands ("show the runners", "open bonk", "filter liquidity over 50k") run locally; questions go to `claude`. Voice only PROPOSES trades — it can never confirm, sign, arm or raise a cap. macOS only; every voice action also has a key. `--sound on|off` sets the sound/voice output at launch.' },
|
|
143
|
+
{ id: 'turn-on-alerts', title: 'Turn on alerts (Telegram / Discord / desktop)', screen: 'all', tags: ['alerts', 'alert', 'notify', 'notification', 'telegram', 'discord', 'desktop', 'channel'],
|
|
144
|
+
actions: [{ action: 'alert.propose', screen: 'screener' }], commands: ['alerts'], feature: 'push-alerts',
|
|
145
|
+
body: 'Use `dexterm alerts` to see channels and enable them; `dexterm alerts set-telegram` / `set-discord` store the bot token / webhook in `~/.dexterm/` (mode 0600) — never in config.json, argv or a log. In-app, `b` proposes a liquidity alert on the highlighted coin and `y` arms it. The analyst can never change alert channels or see their secrets.' },
|
|
146
|
+
{ id: 'set-a-filter', title: 'Filter the screener', screen: 'screener', tags: ['filter', 'screen', 'query', 'search', 'grammar', 'volume', 'liquidity'],
|
|
147
|
+
actions: [{ action: 'filter.edit', screen: 'screener' }], flags: ['--filter'], feature: 'filter-grammar',
|
|
148
|
+
body: 'Press `/` to open the filter editor and type the grammar, e.g. `vol24 > 250K liq > 50K age < 30d org >= 60 launchpad:pump !flag:mint`. Errors show inline with the column. One-shot: `--filter "…"`. The analyst can set a filter for you (it shows "analyst did X").' },
|
|
149
|
+
{ id: 'sort-the-screener', title: 'Sort the screener', screen: 'screener', tags: ['sort', 'order', 'rank', 'column', 'menu'],
|
|
150
|
+
actions: [{ action: 'sort.menu', screen: 'screener' }], flags: ['--sort'], feature: 'filter-grammar',
|
|
151
|
+
body: 'Press `s` for the sort menu: j/k pick a key, ⏎ on the current key flips the direction (ascending/descending). One-shot at launch: `--sort <key>`.' },
|
|
152
|
+
{ id: 'use-presets', title: 'Use screener presets', screen: 'screener', tags: ['preset', 'presets', 'trending', 'runners', 'organic', 'graduating', 'smart'],
|
|
153
|
+
actions: [{ action: 'preset.next', screen: 'screener' }, { action: 'preset.prev', screen: 'screener' }], flags: ['--preset'], feature: 'presets',
|
|
154
|
+
body: 'Press `]` / `[` to step through presets (Trending, Runners, Organic; Graduating ships keyless as an `approx` mcap-band proxy; Smart money needs a key and says so). The analyst can switch the feed too. One-shot at launch: `--preset <name>`.' },
|
|
155
|
+
{ id: 'watch-a-coin', title: 'Watch (or unwatch) a coin', screen: 'all', tags: ['watch', 'watchlist', 'follow', 'track', 'token', 'tokens', 'coins'],
|
|
156
|
+
actions: [{ action: 'watch.add', screen: 'screener' }], feature: 'watchlist',
|
|
157
|
+
body: 'Press `w` to watch/unwatch the highlighted coin (screener, new pairs, token screen). The watchlist is persisted and priced live. On the watchlist screen `d` removes a row.' },
|
|
158
|
+
{ id: 'open-chart-popup', title: 'Open the chart popup', screen: 'token', tags: ['chart', 'popup', 'candles', 'window', 'dock', 'graph'],
|
|
159
|
+
actions: [{ action: 'popup.chart', screen: 'token' }], feature: 'popup-chart',
|
|
160
|
+
body: 'Press `C` on the token screen to open the docked chart popup (a native window, or your browser as fallback). It is fed only by the terminal\'s caches over one loopback WebSocket and never sees your keys. The analyst can open or close it for you.' },
|
|
161
|
+
{ id: 'switch-theme', title: 'Switch the theme', screen: 'all', tags: ['theme', 'themes', 'colour', 'colours', 'color', 'colors', 'look', 'cyberspace', 'midnight', 'ember', 'paper'],
|
|
162
|
+
actions: [{ action: 'theme.next' }], commands: ['theme'], flags: ['--theme'], feature: 'themes-keymaps',
|
|
163
|
+
body: 'Press `t` to cycle themes (cyberspace → midnight → ember → paper). `dexterm theme list|show|init|check` manages your own themes in `~/.dexterm/themes/`; a shared theme cannot hide a warning or swap buy/sell colours (a contrast + hue guard). The analyst can propose a theme change you confirm with a keypress.' },
|
|
164
|
+
{ id: 'calm-mode', title: 'Turn on calm mode (effects off)', screen: 'all', tags: ['calm', 'effects', 'rain', 'glitch', 'quiet', 'still', 'intensity'],
|
|
165
|
+
flags: ['--calm', '--intensity'], feature: 'look',
|
|
166
|
+
body: 'Press `c` to toggle calm mode: rain, glitch, blink and scanlines off, back to the last intensity when pressed again. Start calm with `--calm` (or `DEXTERM_CALM=1`); `--intensity normal|fever|off` picks a tier. The analyst can toggle calm for you.' },
|
|
167
|
+
{ id: 'price-or-marketcap', title: 'Show price or market cap', screen: 'all', tags: ['price', 'mcap', 'marketcap', 'market', 'display', 'usd', 'cap'],
|
|
168
|
+
actions: [{ action: 'display.priceMode' }], commands: ['display'], flags: ['--price-mode'], feature: 'price-display-mode',
|
|
169
|
+
body: 'Press `$` to flip every price between USD price and MARKET CAP (screener, watchlist, new pairs, token, both charts, order-plan levels). Saved as `display.priceMode`; also `dexterm display price|mcap` or `--price-mode`. A coin with no known supply stays on price and says so. The analyst can set this for you.' },
|
|
170
|
+
{ id: 'scan-mode', title: 'Use AI scan mode', screen: 'all', tags: ['scan', 'watcher', 'observe', 'auto', 'ai', 'monitor'],
|
|
171
|
+
actions: [{ action: 'scan.toggle' }, { action: 'scan.resume' }], flags: ['--scan'], feature: 'scan',
|
|
172
|
+
body: 'Scan mode is an observe-only AI watcher (off by default, zero extra requests). Toggle it with `Z` (or `/scan on|off` on the screener line, or `--scan` at launch); `R` resumes a paused scan. It can only surface findings, never take a UI action or trade (structurally observe-only). The analyst can turn it on or off for you.' },
|
|
173
|
+
{ id: 'larp-check', title: 'Run the LARP check (is the product real?)', screen: 'token', tags: ['larp', 'fake', 'real', 'legit', 'website', 'product', 'domain', 'github', 'vet'],
|
|
174
|
+
tools: ['larp_check', 'get_links', 'fetch_page', 'check_domain', 'check_github'], feature: 'larp-check',
|
|
175
|
+
body: 'The LARP check vets whether a real product exists (website, domain age, GitHub) read-only — it is not a price call. Ask the analyst "is <coin> a larp?" (skill `larp-check`), or an agent can call the `larp_check` MCP tool. A grade is a risk flag, never a certificate; it never recommends buy/sell.' },
|
|
176
|
+
{ id: 'rug-check', title: 'Check if a coin is a rug', screen: 'token', tags: ['rug', 'safe', 'honeypot', 'scam', 'risk', 'check', 'authority', 'mint'],
|
|
177
|
+
actions: [{ action: 'analyst.toggle' }], tools: ['analyze_token', 'get_dossier', 'rugcheck'], feature: 'analyst',
|
|
178
|
+
body: 'Press `a` to run a rug-check on the selected coin: authorities, token program, LP, holder concentration, creator history and tape, with a p(rug) from the H1 heuristic (a ranking, not a calibrated probability). The verdict is WATCH / SPECULATIVE / AVOID. Facts come only from tools; it is not financial advice.' },
|
|
179
|
+
{ id: 'ask-the-analyst', title: 'Ask the analyst a question', screen: 'analyst', tags: ['analyst', 'ask', 'claude', 'ai', 'question', 'dossier', 'chat'],
|
|
180
|
+
actions: [{ action: 'analyst.toggle' }, { action: 'open', screen: 'screener' }], tools: ['get_dossier', 'kb_search', 'app_help', 'case_lookup'], feature: 'analyst',
|
|
181
|
+
flags: ['--no-ai'],
|
|
182
|
+
body: 'The analyst is your own `claude` login. ⏎ on a coin runs a full dossier; `a` runs a rug-check (or a trench-scan of the screener when nothing is selected). Type a follow-up and ⏎ to send. If `claude` is missing or logged out the panel shows the one-line fix and the rest of dexterm keeps working. Start without AI using `--no-ai`.' },
|
|
183
|
+
{ id: 'connect-an-agent', title: 'Connect your own agent (MCP)', screen: 'all', tags: ['mcp', 'agent', 'agents', 'connect', 'claude', 'tool', 'server', 'bot'],
|
|
184
|
+
commands: ['mcp'], tools: ['screen_state', 'get_dossier', 'kb_search', 'app_help', 'app_state'], feature: 'mcp',
|
|
185
|
+
body: '`dexterm mcp` is a plain stdio MCP server exposing the same read-only tools the analyst uses. Add it to any Claude Code with `claude mcp add dexterm -- node packages/mcp/bin/dexterm-mcp.mjs`. Agents are untrusted: every tool is read-only and an agent can never sign, arm, raise a cap or touch keys. Use `app_help` for "how do I" questions and `app_state` to see what the user sees.' },
|
|
186
|
+
{ id: 'configure-rpc-key', title: 'Configure a Helius / RPC key for holders', screen: 'all', tags: ['helius', 'rpc', 'key', 'holders', 'free', 'api', 'ranked'],
|
|
187
|
+
feature: 'keyless',
|
|
188
|
+
body: 'dexterm is free-forever and keyless by default. The one real keyless gap is the ranked top-holder list: add a free RPC key for it. Save a Helius key ONLY at `~/.dexterm/helius.key` (mode 600, never a repo or command line), or set `DEXTERM_RPC_URL` to any read-only RPC. `dexterm doctor` prints the exact command. Without a key the holders panel shows count + top-10% via Jupiter.' },
|
|
189
|
+
{ id: 'workspaces', title: 'Save and switch workspaces', screen: 'all', tags: ['workspace', 'workspaces', 'layout', 'hud', 'save', 'morning'],
|
|
190
|
+
actions: [{ action: 'workspace.save' }], commands: ['workspace'], flags: ['--workspace', '--screen'], feature: 'workspaces',
|
|
191
|
+
body: 'A workspace saves a start screen, theme, intensity, screener preset/filter/sort, token panels and the HUD popups to open together. `ctrl+w` then `s` saves as; `w` lists; `n`/`N` cycle. `dexterm workspace list|show|remove|init` manages them. The analyst can open a saved workspace for you.' },
|
|
192
|
+
{ id: 'custom-keymap', title: 'Customize the keymap', screen: 'all', tags: ['keymap', 'keybind', 'keybinding', 'rebind', 'shortcut', 'keys', 'remap'],
|
|
193
|
+
commands: ['keymap'], feature: 'themes-keymaps',
|
|
194
|
+
body: '`config.keymap` in `~/.dexterm/config.json` re-binds actions to keys; `dexterm keymap [screen]` shows the effective bindings including your overrides. Reserved keys (ctrl+c, esc, enter, y, !) and the confirm/dismiss/panic/quit actions cannot be rebound, and collisions refuse the whole keymap.' },
|
|
195
|
+
{ id: 'paper-trade', title: 'Paper trade a coin', screen: 'token', tags: ['paper', 'trade', 'buy', 'sell', 'ticket', 'simulate', 'practice'],
|
|
196
|
+
actions: [{ action: 'trade.buy', screen: 'token' }, { action: 'trade.sell', screen: 'token' }], feature: 'paper-ticket',
|
|
197
|
+
body: 'Press `B` / `S` on the token screen to open a PAPER buy/sell ticket in its editing phase. dexterm holds no keys and signs nothing — paper trading is a simulation. Confirming a ticket needs your explicit keypress (the gate); a stray key never confirms. The analyst can open a PAPER ticket but can never size, confirm or send it.' },
|
|
198
|
+
{ id: 'run-doctor', title: 'Diagnose problems with doctor', screen: 'all', tags: ['doctor', 'diagnose', 'check', 'troubleshoot', 'broken', 'fix', 'problem'],
|
|
199
|
+
commands: ['doctor'], feature: 'doctor',
|
|
200
|
+
body: '`dexterm doctor` is the first stop for any problem: non-interactive, never touches your keys, exits 0 when dexterm will run and 1 when something must be fixed. Every non-pass line prints the exact fix. `dexterm doctor --offline` skips the network probes.' },
|
|
201
|
+
{ id: 'update-dexterm', title: 'Update dexterm', screen: 'all', tags: ['update', 'upgrade', 'version', 'latest', 'new'],
|
|
202
|
+
commands: ['update'], feature: 'update-check',
|
|
203
|
+
body: '`dexterm update` updates the app; a daily notice tells you when a new version is out. The analyst can never install or update software.' },
|
|
204
|
+
{ id: 'edit-kb-or-skill', title: 'Edit the knowledge base or an analyst skill', screen: 'all', tags: ['kb', 'knowledge', 'skill', 'edit', 'customize', 'override', 'analyst'],
|
|
205
|
+
commands: ['kb', 'skill'], tools: ['kb_lookup', 'kb_search'], feature: 'kb',
|
|
206
|
+
body: 'Your own KB entries and skills live in `~/.dexterm/kb/entries/<id>.md` and `~/.dexterm/skills/<name>.md` (same frontmatter as the bundled ones). `dexterm kb|skill list|show|new|edit|check|reset|path`: `edit` copies the bundled file and opens your editor, `check` validates it, `reset` restores the bundled one. A skill is read-only guidance and may never name a trade/sign/arm tool.' },
|
|
207
|
+
{ id: 'backtest', title: 'Backtest the rug model or skills', screen: 'all', tags: ['backtest', 'eval', 'accuracy', 'model', 'metrics', 'history'],
|
|
208
|
+
commands: ['backtest'], tools: ['rug_backtest'], feature: 'skill-backtest',
|
|
209
|
+
body: '`dexterm backtest --subject heuristic|skills|larp|pack_verdicts` scores a subject against the recorder\'s outcome-labelled history and prints honest metrics (precision/recall, Brier vs base rate, AUC, calibration) with leakage checks. With no labelled outcomes it says so and prints no metrics rather than fabricating any.' },
|
|
210
|
+
{ id: 'coin-images', title: 'Turn coin images on or off', screen: 'all', tags: ['images', 'image', 'pictures', 'icons', 'logos', 'off'],
|
|
211
|
+
flags: ['--no-images'], feature: 'coin-images',
|
|
212
|
+
body: 'Coin pictures render as real pixels in image-capable terminals (kitty/iTerm2/sixel) and as block art everywhere else. Turn them off with `--no-images` (or `DEXTERM_IMAGES=0`, or `enabled:false` in `~/.dexterm/images.json`). `dexterm doctor` has a Coin images line that says which path you get.' },
|
|
213
|
+
{ id: 'bubble-map', title: 'Open the bubble map', screen: 'token', tags: ['bubble', 'bubbles', 'map', 'cluster', 'clusters', 'holders', 'wallets', 'cabal'],
|
|
214
|
+
actions: [{ action: 'intel.bubbles', screen: 'token' }], feature: 'bubble-map',
|
|
215
|
+
body: 'Press `M` on a token to open the BUBBLE MAP: top holders as discs (area = share of supply, colour = the rule that labelled the wallet), linked-cluster rings, a CLUSTERS table and a WHY THIS LABEL panel. Keyless it uses the public holder list with static labels; a free Helius key adds funders and the launch window.' },
|
|
216
|
+
{ id: 'what-dexterm-shows-agents', title: 'See what the user currently sees (app_state)', screen: 'all', tags: ['state', 'current', 'screen', 'context', 'selected', 'agent', 'see'],
|
|
217
|
+
tools: ['app_state', 'screen_state'], feature: 'mcp',
|
|
218
|
+
body: 'An agent can call the read-only `app_state` MCP tool to fit an answer to what the user sees: the current screen, the selected coin, the theme, price mode, voice name/rate, whether scan and calm are on, and which alert channels are configured (YES/NO only — never a token, webhook or key). `screen_state` adds the visible rows and the action allow-list.' },
|
|
219
|
+
// --- user-facing subcommand topics (task-1413 r2: forward subcommand/flag coverage)
|
|
220
|
+
{ id: 'app-help-command', title: 'Get help on how dexterm works (dexterm help)', screen: 'all', tags: ['help', 'how', 'topic', 'topics', 'guide', 'manual', 'docs', 'commands'],
|
|
221
|
+
commands: ['help'], flags: ['--help'], feature: 'app-help',
|
|
222
|
+
body: '`dexterm help [topic]` answers "how do I ..." from the generated app-help knowledge base — the same entries the analyst\'s `app_help` tool uses, so they cannot drift from the app. `dexterm help` lists the topics; `dexterm help <topic|key|command>` prints the matching entry with the exact key or command. `--help` (or `-h`) on any command prints its usage. The analyst searches app-help first and never invents a key.' },
|
|
223
|
+
{ id: 'connect-an-agent-registry', title: 'Manage connected agents (dexterm agent)', screen: 'all', tags: ['agent', 'agents', 'registry', 'tier', 'pause', 'resume', 'kick', 'audit', 'bus', 'serve'],
|
|
224
|
+
commands: ['agent'], feature: 'mcp',
|
|
225
|
+
body: '`dexterm agent add|list|rotate|remove|tier|pause|resume|kick|audit|packs` manages your bring-your-own-agent registry and the loopback bus; `dexterm agent serve` runs a standalone bus on fixtures and `connect` attaches one. Every agent is untrusted and read-only: it can never sign, arm, raise a cap, touch keys or change network settings.' },
|
|
226
|
+
{ id: 'spectate-an-agent', title: 'Watch an agent drive dexterm (dexterm spectate)', screen: 'all', tags: ['spectate', 'spectator', 'watch', 'agent', 'cook', 'observe', 'replay', 'stream', 'record', 'mcp', 'follow'],
|
|
227
|
+
commands: ['spectate'], flags: ['--replay', '--spectate-mcp'], feature: 'spectator',
|
|
228
|
+
body: 'Watch an agent work. For an agent on the loopback bus, `dexterm agent serve --spectator-popup` opens the AGENT ACTIVITY window, `--spectate` starts the read-only, token-gated, loopback stream, and `--record FILE` saves a redacted session. For an agent connected over MCP (`dexterm mcp install`), run the app with `dexterm --spectate-mcp`: the running terminal then follows the agent — a navigation call moves the screen, a status line shows `agent <name>: opened <coin>`, ctrl+p pauses, ctrl+k stops and any key you press takes control. `dexterm spectate --replay FILE` replays a saved session step by step and `dexterm spectate --info` shows the live stream endpoint. Every shared copy is redacted — a tool name, refusal, agent line or navigation can never leak a wallet or key — and a spectator can only watch, never act. Full docs: https://dexterm.xyz/docs/spectator/' },
|
|
229
|
+
{ id: 'recorder-history', title: 'Work with the history recorder (dexterm recorder)', screen: 'all', tags: ['recorder', 'record', 'history', 'stats', 'label', 'prune', 'outcomes', 'database', 'session', 'capture', 'data'],
|
|
230
|
+
commands: ['recorder'], flags: ['--record', '--no-recorder'], feature: 'recorder',
|
|
231
|
+
body: '`dexterm recorder stats|label|prune|export` works with the local history recorder\'s tables (the outcome-labelled data the rug model and backtests learn from). No network. Recording is on by default; `--no-recorder` disables it for a run and `--record FILE` captures a data session to a file for replay.' },
|
|
232
|
+
{ id: 'record-headless', title: 'Keep the recorder running with the app closed (dexterm record)', screen: 'all', tags: ['collector', 'collect', 'background', 'headless', 'launchd', 'daemon', 'keep', 'closed', 'backtest'],
|
|
233
|
+
commands: ['record'], feature: 'recorder',
|
|
234
|
+
body: '`dexterm record` runs the history recorder with no terminal UI, so the data the backtests learn from keeps growing while dexterm is closed. `dexterm record status` shows what it has collected; `dexterm record install --dry-run` prints the user-level LaunchAgent it would create (macOS, no sudo) and `dexterm record uninstall` removes it. It uses the same free-tier request limits as the app, never trades, and a disk cap pauses new observations when the database gets large.' },
|
|
235
|
+
{ id: 'narrative-board', title: 'See the narrative board (dexterm narrative)', screen: 'trending', tags: ['narrative', 'narratives', 'board', 'trending', 'movers', 'window', 'summary'],
|
|
236
|
+
commands: ['narrative'], feature: 'trending-board',
|
|
237
|
+
body: '`dexterm narrative [--window 1h|24h] [--summary]` prints the narrative board — what is trending across the recorded feed — with no network and no model. In-app it is the trending screen.' },
|
|
238
|
+
{ id: 'kol-lists', title: 'Manage KOL wallet lists (dexterm kol)', screen: 'kol', tags: ['kol', 'kols', 'influencer', 'leader', 'list', 'lists', 'wallet', 'mute', 'import'],
|
|
239
|
+
commands: ['kol'], feature: 'kol-lists',
|
|
240
|
+
body: '`dexterm kol list|create|add|remove|mute|unmute|enable|disable|import` manages key-opinion-leader wallet lists on the shared DB (official / authorized sources only). In-app the KOL screen shows the lists and their activity.' },
|
|
241
|
+
{ id: 'login-with-x', title: 'Connect your X account (dexterm x)', screen: 'all', tags: ['x', 'twitter', 'login', 'oauth', 'account', 'social', 'connect', 'client'],
|
|
242
|
+
commands: ['x'], feature: 'login-with-x',
|
|
243
|
+
body: '`dexterm x status|set-client-id|login|logout` connects your X (Twitter) account via OAuth2 PKCE on a loopback callback. Tokens stay in `~/.dexterm/x` (0600), are never printed, and the scopes are read-only. It only improves social signal; nothing is posted.' },
|
|
244
|
+
{ id: 'edit-rules', title: 'Set up alert rules (dexterm rules)', screen: 'all', tags: ['rules', 'rule', 'trigger', 'condition', 'alert', 'add', 'dry-run', 'fields', 'engine'],
|
|
245
|
+
commands: ['rules'], feature: 'rules-engine',
|
|
246
|
+
body: '`dexterm rules list|add|remove|enable|disable|check|dry-run|fields` configures the rules engine — the conditions that raise an alert (e.g. liquidity jump, volume spike). `dry-run` tests a rule against recent data; `fields` lists what you can match on. No network, no model, and a rule can never trade. Deliver the alerts with `dexterm alerts` and `dexterm watch`.' },
|
|
247
|
+
{ id: 'watch-headless', title: 'Run alerts in the background (dexterm watch)', screen: 'all', tags: ['watch', 'background', 'headless', 'push', 'alerts', 'notify', 'daemon', 'interval'],
|
|
248
|
+
commands: ['watch'], flags: ['--interval'], feature: 'push-alerts',
|
|
249
|
+
body: '`dexterm watch [--interval S] [--once|--ticks N] [--replay FILE]` runs the rules engine headless and PUSHES alerts to your enabled channels, so they reach you when the terminal is closed. It has no signer and can never trade. Set channels up first with `dexterm alerts`.' },
|
|
250
|
+
{ id: 'share-packs', title: 'Share or install packs (dexterm pack)', screen: 'all', tags: ['pack', 'packs', 'share', 'install', 'export', 'sign', 'trust', 'lock', 'bundle'],
|
|
251
|
+
commands: ['pack'], feature: 'packs',
|
|
252
|
+
body: '`dexterm pack list|verify|add|remove|export|lock|sign|trust|signers|keymap|widget` shares themes, keymaps, rules and widgets as plain files with a sha256 lock and an optional ssh signature. A pack never runs code: widgets land disabled, unsigned rules load disabled, and a keymap merges only on the explicit command.' },
|
|
253
|
+
{ id: 'link-a-wallet', title: 'Link a wallet for trading (dexterm wallet)', screen: 'all', tags: ['wallet', 'link', 'unlink', 'phantom', 'connect', 'pubkey', 'caps', 'trade'],
|
|
254
|
+
commands: ['wallet'], flags: ['--mainnet', '--max-usd', '--daily-usd', '--max-sol-out', '--max-slippage-bps', '--trade-rpc', '--no-live'], feature: 'trading',
|
|
255
|
+
body: '`dexterm wallet link|unlink|status` links a Phantom wallet through a loopback bridge — your keys NEVER enter dexterm, only the public key is stored — and shows the link and the signer-side trade caps. dexterm is non-custodial: it builds and simulates transactions, your own wallet signs. The signer-side caps (`--max-usd`, `--daily-usd`, `--max-sol-out`, `--max-slippage-bps`) and the RPC (`--trade-rpc`) bound every trade and live on the signer; `--mainnet` opts in to mainnet and `--no-live` keeps it paper-only. The analyst can never link, unlink, sign, arm or change a cap.' },
|
|
256
|
+
{ id: 'order-plans', title: 'Set conditional order plans (dexterm plan)', screen: 'all', tags: ['plan', 'plans', 'order', 'conditional', 'take-profit', 'stop', 'trigger', 'arm'],
|
|
257
|
+
commands: ['plan'], feature: 'order-plans',
|
|
258
|
+
body: '`dexterm plan list|add|remove|arm|disarm` sets conditional order plans (e.g. take-profit / stop levels). Voice and agents can only PROPOSE a plan; ARMING one needs your physical keypress, and the caps and panic-stop stay signer-side. Nothing here lets a model sign or raise a cap.' },
|
|
259
|
+
{ id: 'standing-mandates', title: 'Set a standing mandate (dexterm mandate)', screen: 'all', tags: ['mandate', 'mandates', 'standing', 'rule', 'budget', 'auto', 'arm', 'cap'],
|
|
260
|
+
commands: ['mandate'], feature: 'order-plans',
|
|
261
|
+
body: '`dexterm mandate list|add|remove|arm|disarm` sets a standing mandate — a longer-lived conditional rule bounded by a budget. As with plans, a model may only PROPOSE; arming needs your physical keypress and the signer-side caps bound it. The analyst can never arm a mandate or change its caps.' },
|
|
262
|
+
{ id: 'follow-a-wallet', title: 'Copy / follow a wallet (dexterm follow)', screen: 'all', tags: ['follow', 'copy', 'mirror', 'wallet', 'trader', 'track', 'unfollow', 'list'],
|
|
263
|
+
commands: ['follow'], feature: 'copy-trading',
|
|
264
|
+
body: '`dexterm follow list|add|remove` is the copy-trading follow list: local, self-directed and read-only. When a followed wallet trades, dexterm can surface a one-key MIRROR TICKET you confirm yourself — it never auto-trades, never signs, and the analyst can never add a follow or confirm a mirror.' },
|
|
265
|
+
{ id: 'chart-popup-selftest', title: 'Run the popup self-test (dexterm popup --self-test)', screen: 'all', tags: ['self-test', 'selftest', 'test', 'shell', 'fps', 'diagnose', 'hotkey'],
|
|
266
|
+
commands: ['popup'], feature: 'popup-selftest',
|
|
267
|
+
body: '`dexterm popup --self-test [--browser] [--seconds N] [--theme NAME]` opens a chart popup on fixture data and reports the shell, hotkey and fps it can measure headlessly, labelling what still needs a human at an unlocked Mac. In-app, open the popup for real with `C` on the token screen (see "Open the chart popup").' },
|
|
268
|
+
];
|
|
269
|
+
function fm(order) {
|
|
270
|
+
const o = {};
|
|
271
|
+
for (const [k, v] of order)
|
|
272
|
+
if (v !== undefined && !(Array.isArray(v) && v.length === 0) && v !== '')
|
|
273
|
+
o[k] = v;
|
|
274
|
+
return o;
|
|
275
|
+
}
|
|
276
|
+
/** Build every app-help entry from the current app sources + the committed TOPICS/SCREENS tables. */
|
|
277
|
+
export function buildAppHelpEntries() {
|
|
278
|
+
const subcommands = new Set(appSubcommands());
|
|
279
|
+
const flags = new Set(appFlags());
|
|
280
|
+
const tools = new Set(appTools());
|
|
281
|
+
const out = [];
|
|
282
|
+
// --- per-screen entries
|
|
283
|
+
for (const s of SCREENS) {
|
|
284
|
+
const { actions, keys } = screenActions(s.screen);
|
|
285
|
+
const rows = helpRows(s.screen, false);
|
|
286
|
+
const keyList = rows.filter((r) => r.keys && r.label).map((r) => ` \`${r.keys}\` — ${r.label}`).join('\n');
|
|
287
|
+
const toolsUsed = s.tools.filter((t) => tools.has(t));
|
|
288
|
+
const body = `${s.blurb}\n\nKeys on this screen:\n${keyList}`;
|
|
289
|
+
out.push({
|
|
290
|
+
id: `screen-${s.screen}`,
|
|
291
|
+
frontmatter: fm([
|
|
292
|
+
['id', `screen-${s.screen}`], ['title', s.title], ['kind', 'app-help'], ['screen', s.screen],
|
|
293
|
+
['feature', s.feature], ['tags', ['app-help', 'screen', s.screen, ...s.title.toLowerCase().split(/\s+/)]],
|
|
294
|
+
['keys', keys], ['actions', actions], ['tools', toolsUsed],
|
|
295
|
+
['confidence', 'high'], ['owner', 'maintainers'], ['origin', 'app-help-generated'],
|
|
296
|
+
['reviewed_by', `generated@${GENERATED_DATE}`],
|
|
297
|
+
['generated_from', 'dexterm/app keymap + overlay + mcp tools (task-1413)'],
|
|
298
|
+
['sources', ['packages/tui/src/keymap.ts', 'packages/cli/src/screens/overlay.ts']],
|
|
299
|
+
['date', GENERATED_DATE],
|
|
300
|
+
]),
|
|
301
|
+
body,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
// --- topic entries
|
|
305
|
+
for (const t of TOPICS) {
|
|
306
|
+
const keys = [];
|
|
307
|
+
const actions = [];
|
|
308
|
+
const keyLines = [];
|
|
309
|
+
for (const a of t.actions ?? []) {
|
|
310
|
+
const k = keyFor(a.action, a.screen);
|
|
311
|
+
if (!actions.includes(a.action))
|
|
312
|
+
actions.push(a.action);
|
|
313
|
+
for (const kk of k.keys)
|
|
314
|
+
if (!keys.includes(kk))
|
|
315
|
+
keys.push(kk);
|
|
316
|
+
if (k.display)
|
|
317
|
+
keyLines.push(`\`${k.display}\`${a.screen ? ` (${a.screen})` : ''}`);
|
|
318
|
+
}
|
|
319
|
+
// Validate referenced commands/flags/tools exist in the app (fail the generator loudly if not).
|
|
320
|
+
const cmds = (t.commands ?? []).filter((c) => { if (!subcommands.has(c))
|
|
321
|
+
throw new Error(`app-help topic ${t.id}: unknown subcommand "${c}"`); return true; });
|
|
322
|
+
const fls = (t.flags ?? []).filter((f) => { if (!flags.has(f))
|
|
323
|
+
throw new Error(`app-help topic ${t.id}: unknown flag "${f}"`); return true; });
|
|
324
|
+
const tls = (t.tools ?? []).filter((x) => { if (!tools.has(x))
|
|
325
|
+
throw new Error(`app-help topic ${t.id}: unknown MCP tool "${x}"`); return true; });
|
|
326
|
+
const trailer = [];
|
|
327
|
+
if (keyLines.length)
|
|
328
|
+
trailer.push(`Keys: ${keyLines.join(', ')}.`);
|
|
329
|
+
if (cmds.length)
|
|
330
|
+
trailer.push(`CLI: ${cmds.map((c) => `\`dexterm ${c}\``).join(', ')}.`);
|
|
331
|
+
const body = trailer.length ? `${t.body}\n\n${trailer.join(' ')}` : t.body;
|
|
332
|
+
out.push({
|
|
333
|
+
id: t.id,
|
|
334
|
+
frontmatter: fm([
|
|
335
|
+
['id', t.id], ['title', t.title], ['kind', 'app-help'], ['screen', t.screen],
|
|
336
|
+
['feature', t.feature ?? ''], ['tags', ['app-help', ...t.tags]],
|
|
337
|
+
['keys', keys], ['actions', actions], ['commands', cmds], ['flags', fls], ['tools', tls],
|
|
338
|
+
['confidence', 'high'], ['owner', 'maintainers'], ['origin', 'app-help-generated'],
|
|
339
|
+
['reviewed_by', `generated@${GENERATED_DATE}`],
|
|
340
|
+
['generated_from', 'dexterm/app keymap + overlay + mcp tools + cli (task-1413)'],
|
|
341
|
+
['sources', ['packages/tui/src/keymap.ts', 'packages/cli/src/main.ts', 'packages/mcp/src/server.ts']],
|
|
342
|
+
['date', GENERATED_DATE],
|
|
343
|
+
]),
|
|
344
|
+
body,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
out.sort((a, b) => a.id.localeCompare(b.id));
|
|
348
|
+
return out;
|
|
349
|
+
}
|
|
350
|
+
/** Serialize an entry to the bundled `.md` format the loader parses (block lists, deterministic order). */
|
|
351
|
+
export function serializeEntry(e) {
|
|
352
|
+
const lines = ['---'];
|
|
353
|
+
for (const [k, v] of Object.entries(e.frontmatter)) {
|
|
354
|
+
if (Array.isArray(v)) {
|
|
355
|
+
lines.push(`${k}:`);
|
|
356
|
+
for (const item of v)
|
|
357
|
+
lines.push(` - ${item}`);
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
lines.push(`${k}: ${v}`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
lines.push('---', '', e.body.trim(), '');
|
|
364
|
+
return lines.join('\n');
|
|
365
|
+
}
|