dexterm-cli 0.1.3 → 0.1.5
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/cli/assets/claude/analyst-system.md +2 -1
- package/dist/packages/cli/src/agent-paper.js +60 -0
- package/dist/packages/cli/src/analyst/backend.js +183 -0
- package/dist/packages/cli/src/analyst/codex.js +301 -0
- package/dist/packages/cli/src/analyst/session.js +9 -2
- package/dist/packages/cli/src/app-help-gen.js +18 -9
- package/dist/packages/cli/src/docs-kb-gen.js +228 -0
- package/dist/packages/cli/src/doctor.js +80 -9
- 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/dev-history.js +99 -0
- package/dist/packages/cli/src/live/newpairs.js +12 -0
- package/dist/packages/cli/src/live/screens/token-live.js +11 -1
- package/dist/packages/cli/src/live/token.js +39 -1
- package/dist/packages/cli/src/main.js +260 -7
- package/dist/packages/cli/src/mcp-follow.js +255 -0
- package/dist/packages/cli/src/record-cmd.js +2 -0
- package/dist/packages/cli/src/rules.js +25 -2
- package/dist/packages/cli/src/screens/analyst-session.js +5 -3
- package/dist/packages/cli/src/screens/analyst.js +4 -3
- package/dist/packages/cli/src/spectate-cmd.js +14 -3
- package/dist/packages/cli/src/version.js +1 -1
- package/dist/packages/cli/src/x-cmd.js +11 -5
- package/dist/packages/custom/src/rules.js +2 -2
- package/dist/packages/custom/src/schema.js +5 -1
- package/dist/packages/data/src/fixtures.js +28 -6
- package/dist/packages/data/src/index.js +3 -0
- package/dist/packages/data/src/sources/jupiter.js +1 -0
- package/dist/packages/data/src/trade/agent/paper-desk.js +336 -0
- package/dist/packages/data/src/trade/agent/rendezvous.js +161 -0
- package/dist/packages/mcp/assets/kb/app-help/agent-paper-trade.md +38 -0
- package/dist/packages/mcp/assets/kb/app-help/ask-the-analyst.md +1 -1
- package/dist/packages/mcp/assets/kb/app-help/connect-an-agent.md +1 -1
- package/dist/packages/mcp/assets/kb/app-help/dev-history.md +32 -0
- package/dist/packages/mcp/assets/kb/app-help/paper-trade.md +1 -1
- package/dist/packages/mcp/assets/kb/app-help/screen-positions.md +4 -1
- package/dist/packages/mcp/assets/kb/app-help/screen-token.md +1 -0
- package/dist/packages/mcp/assets/kb/app-help/spectate-an-agent.md +4 -1
- package/dist/packages/mcp/assets/kb/app-help/use-codex-analyst.md +31 -0
- package/dist/packages/mcp/assets/kb/docs/docs-agents-agent-paper-trading.md +50 -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-directory-metadata-prepared-not-submitted.md +36 -0
- package/dist/packages/mcp/assets/kb/docs/docs-agents-install-in-one-command.md +51 -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-use-codex-instead-of-claude.md +35 -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 +47 -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 +37 -0
- package/dist/packages/mcp/assets/kb/docs/docs-configuration-the-analyst-s-launch.md +29 -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 +81 -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 +53 -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-letting-an-agent-paper-trade-experimental.md +32 -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-dev-history.md +44 -0
- package/dist/packages/mcp/assets/kb/docs/docs-screens-how-well-it-predicts-measured-held-out.md +46 -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 +38 -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 +123 -0
- package/dist/packages/mcp/src/firewall.js +2 -2
- package/dist/packages/mcp/src/index.js +8 -4
- package/dist/packages/mcp/src/paper-agent.js +92 -0
- package/dist/packages/mcp/src/server.js +261 -29
- package/dist/packages/mcp/src/spectator-sink.js +146 -5
- package/dist/packages/mcp/src/user-content.js +1 -0
- package/dist/packages/recorder/src/dev-history.js +129 -0
- package/dist/packages/recorder/src/index.js +1 -0
- package/dist/packages/recorder/src/recorder.js +23 -0
- package/dist/packages/recorder/src/schema.js +11 -0
- package/dist/packages/voice/src/stt/whisper.js +4 -2
- package/package.json +6 -5
|
@@ -85,7 +85,7 @@ export function appTools() {
|
|
|
85
85
|
const SCREENS = [
|
|
86
86
|
{ screen: 'screener', title: 'The screener screen', feature: 'screener', tools: ['screener_query'],
|
|
87
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'],
|
|
88
|
+
{ screen: 'token', title: 'The token screen', feature: 'token-intel', tools: ['get_dossier', 'get_token', 'get_holders', 'get_trades', 'get_candles', 'get_dev_history', 'dev_history', 'rugcheck'],
|
|
89
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
90
|
{ screen: 'newpairs', title: 'The new pairs screen', feature: 'new-pairs', tools: ['screener_query'],
|
|
91
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).' },
|
|
@@ -93,8 +93,8 @@ const SCREENS = [
|
|
|
93
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
94
|
{ screen: 'analyst', title: 'The analyst screen', feature: 'analyst', tools: ['get_dossier', 'kb_search', 'app_help'],
|
|
95
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.' },
|
|
96
|
+
{ screen: 'positions', title: 'The paper positions screen', feature: 'positions', tools: ['paper_positions', 'paper_pnl'],
|
|
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. An agent you enabled can read these over MCP with the read-only paper_positions and paper_pnl tools.' },
|
|
98
98
|
{ screen: 'trending', title: 'The trending screen', feature: 'trending-board', tools: ['narrative_board'],
|
|
99
99
|
blurb: 'What is trending in the space — narratives and movers aggregated from the feed.' },
|
|
100
100
|
{ screen: 'kol', title: 'The KOL lists screen', feature: 'kol-lists', tools: [],
|
|
@@ -179,10 +179,16 @@ const TOPICS = [
|
|
|
179
179
|
{ id: 'ask-the-analyst', title: 'Ask the analyst a question', screen: 'analyst', tags: ['analyst', 'ask', 'claude', 'ai', 'question', 'dossier', 'chat'],
|
|
180
180
|
actions: [{ action: 'analyst.toggle' }, { action: 'open', screen: 'screener' }], tools: ['get_dossier', 'kb_search', 'app_help', 'case_lookup'], feature: 'analyst',
|
|
181
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`.' },
|
|
182
|
+
body: 'The analyst is your own `claude` login (or your own `codex` login: `/engine codex`, see "Run the analyst on Codex"). ⏎ 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
183
|
{ id: 'connect-an-agent', title: 'Connect your own agent (MCP)', screen: 'all', tags: ['mcp', 'agent', 'agents', 'connect', 'claude', 'tool', 'server', 'bot'],
|
|
184
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
|
|
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`, or set up Claude Code, Claude Desktop, Cursor or Codex in one command with `dexterm mcp install <client>`. 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: 'use-codex-analyst', title: 'Run the analyst on Codex instead of Claude', screen: 'all', tags: ['codex', 'openai', 'chatgpt', 'engine', 'analyst', 'claude', 'switch', 'backend'],
|
|
187
|
+
flags: ['--analyst-engine'], feature: 'analyst',
|
|
188
|
+
body: 'The analyst can run on your own Codex CLI login instead of Claude (0.1.5). Pick it with `--analyst-engine codex`, `DEXTERM_ANALYST_ENGINE=codex`, `"analyst": {"engine": "codex"}` in `~/.dexterm/config.json`, or type `/engine codex` in the chat box (new conversations; `/engine` alone shows the engine and what is installed). The default `auto` uses claude when it is installed, else codex. dexterm stores no credentials: your `codex login` does the work. The Codex run is locked read-only: only the read-only dexterm tools (plus the UI actions the session allows, none in scan mode), no shell, no file edits, no web search, no other MCP server; a turn that tries anything else is stopped. `dexterm doctor` shows a Codex CLI line (installed, version, logged in). Codex is also supported as an MCP client: `dexterm mcp install codex`.' },
|
|
189
|
+
{ id: 'dev-history', title: 'Check if a coin\'s creator rugged before (dev history)', screen: 'token', tags: ['dev', 'deployer', 'creator', 'serial', 'rugger', 'rugged', 'history', 'launches'],
|
|
190
|
+
tools: ['dev_history', 'get_dev_history'], feature: 'token-intel',
|
|
191
|
+
body: 'The token screen\'s ON-CHAIN panel shows the creator\'s recorded track record (0.1.5): `Dev rugs (recorded)` (rugs out of labelled prior launches, with the sample n), `Dev launches seen`, `Dev sold its bag` (median minutes to the dev\'s exit) and `Dev linked` (funder or cluster from the wallet-intel scan). It comes from dexterm\'s own local history recorder (keyless creator links + code-only outcome labels), so it says `unknown` when there is no record; it is a risk flag from observed history, never a certificate. Alert on it with the rule field `devrugs`, e.g. `when devrugs >= 2 then alert(warn, "new coin from a wallet that rugged before") scope newpairs`. Agents and the analyst use the `dev_history` MCP tool.' },
|
|
186
192
|
{ id: 'configure-rpc-key', title: 'Configure a Helius / RPC key for holders', screen: 'all', tags: ['helius', 'rpc', 'key', 'holders', 'free', 'api', 'ranked'],
|
|
187
193
|
feature: 'keyless',
|
|
188
194
|
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.' },
|
|
@@ -194,7 +200,10 @@ const TOPICS = [
|
|
|
194
200
|
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
201
|
{ id: 'paper-trade', title: 'Paper trade a coin', screen: 'token', tags: ['paper', 'trade', 'buy', 'sell', 'ticket', 'simulate', 'practice'],
|
|
196
202
|
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.' },
|
|
203
|
+
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 in-app analyst can open a PAPER ticket but can never size, confirm or send it. If you separately arm a mandate and enable agent paper trading, a connected agent can open, size, confirm and close PAPER trades inside your caps (never raising them, never signing) — see "Let an agent paper trade".' },
|
|
204
|
+
{ id: 'agent-paper-trade', title: 'Let an agent paper trade', screen: 'all', tags: ['agent', 'mcp', 'paper', 'autonomous', 'experimental', 'confirm', 'close', 'simulated'],
|
|
205
|
+
flags: ['--agent-paper'], tools: ['paper_open_ticket', 'paper_size_ticket', 'paper_confirm', 'paper_close', 'paper_positions', 'paper_pnl'], feature: 'mcp',
|
|
206
|
+
body: 'Experimental, paper only, off by default. Arm a mandate yourself (its caps, by keypress), then start `dexterm --spectate-mcp --agent-paper <mandate-id>`: an agent connected through `dexterm mcp` can then open, size, confirm and close SIMULATED (paper) trades inside those caps with paper_open_ticket / paper_size_ticket / paper_confirm / paper_close, and read paper_positions / paper_pnl. The agent is untrusted: it can never sign, send a real transaction, touch keys, or raise a cap; its per-trade cap is a quarter of the mandate\'s, and a size over any cap is refused. Caps are re-read from your armed mandate on every call, so revoking it or the panic key stops the agent. Every call is journaled and shows in this terminal\'s agent activity; Ctrl+P pauses, Ctrl+K stops for the rest of the run, any key takes over, and quitting turns it off. Figures are simulated and are not a result.' },
|
|
198
207
|
{ id: 'run-doctor', title: 'Diagnose problems with doctor', screen: 'all', tags: ['doctor', 'diagnose', 'check', 'troubleshoot', 'broken', 'fix', 'problem'],
|
|
199
208
|
commands: ['doctor'], feature: 'doctor',
|
|
200
209
|
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.' },
|
|
@@ -223,9 +232,9 @@ const TOPICS = [
|
|
|
223
232
|
{ 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
233
|
commands: ['agent'], feature: 'mcp',
|
|
225
234
|
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'],
|
|
227
|
-
commands: ['spectate'], flags: ['--replay'], feature: 'spectator',
|
|
228
|
-
body: '`dexterm
|
|
235
|
+
{ 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'],
|
|
236
|
+
commands: ['spectate'], flags: ['--replay', '--spectate-mcp'], feature: 'spectator',
|
|
237
|
+
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
238
|
{ 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
239
|
commands: ['recorder'], flags: ['--record', '--no-recorder'], feature: 'recorder',
|
|
231
240
|
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.' },
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// dexterm — generator for the docs knowledge base (task-1420).
|
|
2
|
+
//
|
|
3
|
+
// Builds the `@dexterm/mcp` docs KB entries (assets/kb/docs/*.md) from EVERY page of the site's docs
|
|
4
|
+
// (dexterm/site/content/docs/*.md), chunked by heading, so the analyst can answer from the docs and cite the
|
|
5
|
+
// exact page section (https://dexterm.xyz/docs/<slug>#<anchor>). The app-help KB (app-help-gen.ts) stays the
|
|
6
|
+
// authority for an exact key or command; these entries carry the docs' explanations and never a structured
|
|
7
|
+
// key/command/flag/tool list (docs text is data: nothing in it becomes a key, a command or an action).
|
|
8
|
+
//
|
|
9
|
+
// The site is a sibling checkout (../site from the app root) that a public checkout does not have: `haveSite`
|
|
10
|
+
// is false there, the drift test skips, and the committed entries still ship.
|
|
11
|
+
//
|
|
12
|
+
// Deterministic by construction: files in name order, no clock, no network, no writes here (the script does
|
|
13
|
+
// that). The site's `{{placeholders}}` are filled from the app at site build time; they are NOT expanded here
|
|
14
|
+
// (that would tie the KB to the app's current commit). `{{minNode}}` comes from site.json; the commit stamp
|
|
15
|
+
// sentences are dropped; every other placeholder becomes a pointer to the docs page section.
|
|
16
|
+
import fs from 'node:fs';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
import { fileURLToPath } from 'node:url';
|
|
19
|
+
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
/** App root: packages/cli/src → up three. */
|
|
21
|
+
const ROOT = path.resolve(HERE, '..', '..', '..');
|
|
22
|
+
/** The site checkout next to the app (absent in a public checkout). `DEXTERM_SITE_DIR` overrides it. */
|
|
23
|
+
export const SITE_DIR = process.env.DEXTERM_SITE_DIR || path.resolve(ROOT, '..', 'site');
|
|
24
|
+
export const haveSite = fs.existsSync(path.join(SITE_DIR, 'content', 'docs'));
|
|
25
|
+
/** Stamped into every generated entry; bump on a deliberate regenerate. */
|
|
26
|
+
export const DOCS_GENERATED_DATE = '2026-10-16';
|
|
27
|
+
export const DOCS_BASE_URL = 'https://dexterm.xyz';
|
|
28
|
+
const SLUG_FILE = /^[a-z0-9][a-z0-9-]*\.md$/;
|
|
29
|
+
const MAX_ID = 64;
|
|
30
|
+
/** The site's heading slug (site/lib/html.mjs `slug`), applied to heading text without backticks. */
|
|
31
|
+
export function siteSlug(s) {
|
|
32
|
+
return String(s).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
33
|
+
}
|
|
34
|
+
/** The site's frontmatter split (site/lib/pages/docs.mjs `frontMatter`): flat `key: value` lines. */
|
|
35
|
+
function siteFrontMatter(src) {
|
|
36
|
+
const m = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(src.replace(/\r/g, ''));
|
|
37
|
+
if (!m)
|
|
38
|
+
return { meta: {}, body: src };
|
|
39
|
+
const meta = {};
|
|
40
|
+
for (const line of m[1].split('\n')) {
|
|
41
|
+
const i = line.indexOf(':');
|
|
42
|
+
if (i > 0)
|
|
43
|
+
meta[line.slice(0, i).trim()] = line.slice(i + 1).trim();
|
|
44
|
+
}
|
|
45
|
+
return { meta, body: m[2] };
|
|
46
|
+
}
|
|
47
|
+
/** One frontmatter value on one line: no newlines, no leading/trailing quote the KB parser would strip. */
|
|
48
|
+
function oneLine(s) {
|
|
49
|
+
return s.replace(/[\u0000-\u001F\u007F]/g, ' ').replace(/\s+/g, ' ').trim().replace(/^"+|"+$/g, '');
|
|
50
|
+
}
|
|
51
|
+
/** Absolute URL for a docs link: `/docs/x/#a` → https://dexterm.xyz/docs/x#a, `#a` → this page. */
|
|
52
|
+
export function docsUrl(href, pageUrl) {
|
|
53
|
+
if (/^https?:\/\//i.test(href))
|
|
54
|
+
return href;
|
|
55
|
+
if (href.startsWith('#'))
|
|
56
|
+
return `${pageUrl}${href}`;
|
|
57
|
+
if (!href.startsWith('/'))
|
|
58
|
+
return href;
|
|
59
|
+
const d = /^\/docs\/([a-z0-9-]+)\/?(#[a-z0-9-]+)?$/.exec(href);
|
|
60
|
+
if (d)
|
|
61
|
+
return `${DOCS_BASE_URL}/docs/${d[1]}${d[2] ?? ''}`;
|
|
62
|
+
return `${DOCS_BASE_URL}${href}`;
|
|
63
|
+
}
|
|
64
|
+
const HTML_TAG = /<\/?(?:kbd|code|a|em|strong|b|i|br|span|div|p|details|summary|table|thead|tbody|tr|td|th|img|ul|ol|li|sup|sub|small|figure|figcaption|hr)\b[^>]*>/gi;
|
|
65
|
+
const TAG_STOP = new Set(['the', 'and', 'for', 'with', 'what', 'how', 'you', 'your', 'its', 'not', 'one', 'see', 'also', 'from', 'into', 'when', 'it', 'is', 'of', 'to', 'in', 'on', 'a', 'an', 'vs', 'by', 'or']);
|
|
66
|
+
/** Prose → KB body: placeholders resolved, links made absolute, inline HTML tags dropped (code fences kept as-is). */
|
|
67
|
+
function cleanBody(lines, ctx) {
|
|
68
|
+
const out = [];
|
|
69
|
+
let fence = false;
|
|
70
|
+
for (const raw of lines) {
|
|
71
|
+
let line = raw;
|
|
72
|
+
if (/^```/.test(line)) {
|
|
73
|
+
fence = !fence;
|
|
74
|
+
out.push(line);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (!fence) {
|
|
78
|
+
// The commit-stamp sentences ("Generated against … at commit {{head}}.") describe the site build, not the app.
|
|
79
|
+
line = line.replace(/(^|(?<=[.!?]\s))[^.!?\n]*\{\{head(?:Date)?\}\}[^\n]*?[.!?](?=\s|$)\s*/g, '');
|
|
80
|
+
line = line.replace(/\[\[([^\]]+)\]\]/g, '`$1`'); // the site's [[key]] → <kbd> shorthand
|
|
81
|
+
line = line.replace(/!\[([^\]]*)\]\([^)]*\)/g, '$1');
|
|
82
|
+
line = line.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (_m, text, href) => `${text} (${docsUrl(href, ctx.pageUrl)})`);
|
|
83
|
+
line = line.replace(HTML_TAG, '');
|
|
84
|
+
}
|
|
85
|
+
line = line.replace(/\{\{minNode\}\}/g, ctx.nodeMin ?? `(see ${ctx.sectionUrl})`);
|
|
86
|
+
line = line.replace(/\{\{[^}]*\}\}/g, `(generated from the app: see ${ctx.sectionUrl}; app-help has the exact keys and commands)`);
|
|
87
|
+
out.push(line.replace(/\s+$/, ''));
|
|
88
|
+
}
|
|
89
|
+
return out.join('\n').replace(/\n{3,}/g, '\n\n').trim();
|
|
90
|
+
}
|
|
91
|
+
function tagsFor(...texts) {
|
|
92
|
+
const out = [];
|
|
93
|
+
for (const t of texts) {
|
|
94
|
+
for (const w of t.replace(/`/g, '').toLowerCase().match(/[a-z0-9][a-z0-9-]+/g) ?? []) {
|
|
95
|
+
if (w.length < 3 || TAG_STOP.has(w) || out.includes(w))
|
|
96
|
+
continue;
|
|
97
|
+
out.push(w);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return out.slice(0, 12);
|
|
101
|
+
}
|
|
102
|
+
/** A KB id ≤ 64 chars (cut at a dash), unique within this run. */
|
|
103
|
+
function uniqueId(base, used) {
|
|
104
|
+
let id = base;
|
|
105
|
+
if (id.length > MAX_ID) {
|
|
106
|
+
id = id.slice(0, MAX_ID);
|
|
107
|
+
const d = id.lastIndexOf('-');
|
|
108
|
+
if (d > 20)
|
|
109
|
+
id = id.slice(0, d);
|
|
110
|
+
}
|
|
111
|
+
let out = id, n = 2;
|
|
112
|
+
while (used.has(out)) {
|
|
113
|
+
const suf = `-${n++}`;
|
|
114
|
+
out = id.slice(0, MAX_ID - suf.length) + suf;
|
|
115
|
+
}
|
|
116
|
+
used.add(out);
|
|
117
|
+
return out;
|
|
118
|
+
}
|
|
119
|
+
/** Split a page into its intro + one section per h2/h3, with anchors exactly as the site renders them. */
|
|
120
|
+
export function splitPage(body) {
|
|
121
|
+
const ids = new Set();
|
|
122
|
+
const uid = (text) => { const base = siteSlug(text.replace(/`/g, '')); let id = base, n = 2; while (ids.has(id))
|
|
123
|
+
id = `${base}-${n++}`; ids.add(id); return id; };
|
|
124
|
+
const lines = body.replace(/\r/g, '').split('\n');
|
|
125
|
+
const intro = [];
|
|
126
|
+
const sections = [];
|
|
127
|
+
let cur = intro;
|
|
128
|
+
for (let i = 0; i < lines.length; i++) {
|
|
129
|
+
const line = lines[i];
|
|
130
|
+
if (/^```/.test(line)) {
|
|
131
|
+
// a fenced block: copied whole, never scanned for headings (mirrors the site renderer)
|
|
132
|
+
cur.push(line);
|
|
133
|
+
for (i++; i < lines.length && !/^```/.test(lines[i]); i++)
|
|
134
|
+
cur.push(lines[i]);
|
|
135
|
+
if (i < lines.length)
|
|
136
|
+
cur.push(lines[i]);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (/^<(?!kbd|code|a |em|strong)[a-z!]/.test(line)) {
|
|
140
|
+
// a raw HTML block (until a blank line): the site does not read headings inside it either
|
|
141
|
+
for (; i < lines.length && lines[i].trim(); i++)
|
|
142
|
+
cur.push(lines[i]);
|
|
143
|
+
i--;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const h = /^(#{1,4})\s+(.+?)\s*(?:\{#([a-z0-9-]+)\})?$/.exec(line);
|
|
147
|
+
if (h) {
|
|
148
|
+
const level = h[1].length;
|
|
149
|
+
const anchor = h[3] ? (ids.add(h[3]), h[3]) : uid(h[2]);
|
|
150
|
+
if (level === 2 || level === 3) {
|
|
151
|
+
const s = { level, heading: h[2].replace(/`/g, ''), anchor, lines: [] };
|
|
152
|
+
sections.push(s);
|
|
153
|
+
cur = s.lines;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
cur.push(line); // h1/h4 stay inside their section as text
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
cur.push(line);
|
|
160
|
+
}
|
|
161
|
+
return { intro, sections };
|
|
162
|
+
}
|
|
163
|
+
/** Every docs page file (name order), or [] when the site is absent. */
|
|
164
|
+
export function docsPages(siteDir = SITE_DIR) {
|
|
165
|
+
const dir = path.join(siteDir, 'content', 'docs');
|
|
166
|
+
try {
|
|
167
|
+
return fs.readdirSync(dir).filter((f) => SLUG_FILE.test(f)).sort();
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/** Build every docs KB entry from the site's docs pages. Throws when the site has no docs directory. */
|
|
174
|
+
export function buildDocsEntries(siteDir = SITE_DIR) {
|
|
175
|
+
const dir = path.join(siteDir, 'content', 'docs');
|
|
176
|
+
if (!fs.existsSync(dir))
|
|
177
|
+
throw new Error(`no site docs at ${dir} (the site checkout is not next to the app)`);
|
|
178
|
+
let nodeMin = null;
|
|
179
|
+
try {
|
|
180
|
+
nodeMin = String(JSON.parse(fs.readFileSync(path.join(siteDir, 'site.json'), 'utf8')).nodeMin ?? '') || null;
|
|
181
|
+
}
|
|
182
|
+
catch { /* no site.json */ }
|
|
183
|
+
const out = [];
|
|
184
|
+
const used = new Set();
|
|
185
|
+
for (const file of docsPages(siteDir)) {
|
|
186
|
+
const slug = file.slice(0, -3);
|
|
187
|
+
const { meta, body } = siteFrontMatter(fs.readFileSync(path.join(dir, file), 'utf8'));
|
|
188
|
+
const pageTitle = oneLine(meta.title || slug);
|
|
189
|
+
const description = oneLine(meta.description || '');
|
|
190
|
+
const pageUrl = `${DOCS_BASE_URL}/docs/${slug}`;
|
|
191
|
+
const { intro, sections } = splitPage(body);
|
|
192
|
+
const entry = (heading, anchor, lines, parent) => {
|
|
193
|
+
const url = anchor ? `${pageUrl}#${anchor}` : pageUrl;
|
|
194
|
+
let text = cleanBody(lines, { pageUrl, sectionUrl: url, nodeMin });
|
|
195
|
+
if (!anchor && description)
|
|
196
|
+
text = text ? `${description}\n\n${text}` : description;
|
|
197
|
+
if (!text)
|
|
198
|
+
return;
|
|
199
|
+
const head = oneLine(heading);
|
|
200
|
+
const title = head ? `${pageTitle} › ${parent ? `${oneLine(parent)} › ` : ''}${head}` : pageTitle;
|
|
201
|
+
const id = uniqueId(anchor ? `docs-${slug}-${anchor}` : `docs-${slug}`, used);
|
|
202
|
+
const fmo = [
|
|
203
|
+
['id', id], ['title', title], ['kind', 'docs'], ['page', slug], ['page_title', pageTitle],
|
|
204
|
+
['heading', head], ['anchor', anchor], ['url', url],
|
|
205
|
+
['tags', ['docs', slug, ...tagsFor(head, parent ?? '', pageTitle).filter((t) => t !== slug && t !== 'docs')]],
|
|
206
|
+
['confidence', 'high'], ['owner', 'maintainers'], ['origin', 'docs-generated'],
|
|
207
|
+
['reviewed_by', `generated@${DOCS_GENERATED_DATE}`],
|
|
208
|
+
['generated_from', 'dexterm/site content/docs (task-1420)'],
|
|
209
|
+
['sources', [`site/content/docs/${file}`]],
|
|
210
|
+
['date', DOCS_GENERATED_DATE],
|
|
211
|
+
];
|
|
212
|
+
const frontmatter = {};
|
|
213
|
+
for (const [k, v] of fmo)
|
|
214
|
+
if (v !== '' && !(Array.isArray(v) && v.length === 0))
|
|
215
|
+
frontmatter[k] = v;
|
|
216
|
+
out.push({ id, frontmatter, body: text });
|
|
217
|
+
};
|
|
218
|
+
entry('', '', intro, null);
|
|
219
|
+
let h2 = null;
|
|
220
|
+
for (const s of sections) {
|
|
221
|
+
if (s.level === 2)
|
|
222
|
+
h2 = s.heading;
|
|
223
|
+
entry(s.heading, s.anchor, s.lines, s.level === 3 ? h2 : null);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
out.sort((a, b) => a.id.localeCompare(b.id));
|
|
227
|
+
return out;
|
|
228
|
+
}
|
|
@@ -17,13 +17,22 @@ import { XTokenStore } from '../../data/src/index.js';
|
|
|
17
17
|
import { SayTts, voiceDoctor, resolveVoice, WHISPER_MODEL, resolvePiperRuntime, pickPlayer, PIPER_MODEL } from '../../voice/src/index.js';
|
|
18
18
|
import { loadConfig, dextermHome } from '../../custom/src/index.js';
|
|
19
19
|
import { kbStore, skillStore } from "./kb-skill-cmd.js";
|
|
20
|
+
import { analystCliOnPath, CODEX_MIN_VERSION, parseEngineChoice, resolveEngine, versionAtLeast } from "./analyst/codex.js";
|
|
20
21
|
/** The minimum Node that runs dexterm (type-stripping of the .ts entry needs 22.18). */
|
|
21
22
|
export const MIN_NODE = [22, 18, 0];
|
|
22
|
-
/** Cheap PATH lookup (no spawn)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
/** Cheap PATH lookup (no spawn). Windows (task-1423): PATH is `;`-separated and commands carry an extension
|
|
24
|
+
* (`claude.cmd` from npm, `claude.exe` from the native installer), so try the PATHEXT list there. */
|
|
25
|
+
export function onPath(bin, env = process.env, platform = process.platform) {
|
|
26
|
+
const win = platform === 'win32';
|
|
27
|
+
// Windows file names are case-insensitive; lower-case keeps the lookup exact on the case-sensitive test hosts
|
|
28
|
+
const exts = win ? ['', ...(env.PATHEXT || '.COM;.EXE;.BAT;.CMD').toLowerCase().split(';').filter(Boolean)] : [''];
|
|
29
|
+
for (const p of (env.PATH ?? env.Path ?? '').split(win ? ';' : ':')) {
|
|
30
|
+
if (!p)
|
|
31
|
+
continue;
|
|
32
|
+
for (const ext of exts)
|
|
33
|
+
if (existsSync(join(p, bin + ext)))
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
27
36
|
return false;
|
|
28
37
|
}
|
|
29
38
|
/**
|
|
@@ -104,7 +113,12 @@ export async function livingProbe(url, timeoutMs = 4000) {
|
|
|
104
113
|
}
|
|
105
114
|
}
|
|
106
115
|
/** Default claude probe: spawn `claude --version` with a timeout. Presence only (login is not asserted here). */
|
|
107
|
-
export async function claudeProbe(timeoutMs = 4000) {
|
|
116
|
+
export async function claudeProbe(timeoutMs = 4000, platform = process.platform, env = process.env) {
|
|
117
|
+
// Windows (task-1423): answer from PATH without a spawn, and count only what the analyst's shell-less spawn can
|
|
118
|
+
// start: `claude.exe` / `.com` (the native installer). An npm `claude.cmd` alone is 'missing', because the
|
|
119
|
+
// analyst cannot run it, so "found on PATH" would be a false pass.
|
|
120
|
+
if (platform === 'win32')
|
|
121
|
+
return onPath('claude', { ...env, PATHEXT: '.EXE;.COM' }, platform) ? 'present' : 'missing';
|
|
108
122
|
const { execFile } = await import('node:child_process');
|
|
109
123
|
return new Promise((resolve) => {
|
|
110
124
|
let done = false;
|
|
@@ -128,6 +142,32 @@ export async function claudeProbe(timeoutMs = 4000) {
|
|
|
128
142
|
}
|
|
129
143
|
});
|
|
130
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Default codex probe (0.1.5): PATH (spawnable form only), then `codex --version` and `codex login status`. The
|
|
147
|
+
* status command is the user's own CLI reporting on its own login; dexterm never reads the credential itself.
|
|
148
|
+
*/
|
|
149
|
+
export async function codexProbe(timeoutMs = 4000, env = process.env) {
|
|
150
|
+
if (!analystCliOnPath('codex', env))
|
|
151
|
+
return { state: 'missing' };
|
|
152
|
+
const { execFile } = await import('node:child_process');
|
|
153
|
+
const run = (args) => new Promise((resolve) => {
|
|
154
|
+
try {
|
|
155
|
+
const child = execFile('codex', args, { timeout: timeoutMs, env }, (err, stdout, stderr) => {
|
|
156
|
+
const code = err ? (typeof err.code === 'number' ? err.code : null) : 0;
|
|
157
|
+
resolve({ code, out: `${stdout ?? ''}${stderr ?? ''}` });
|
|
158
|
+
});
|
|
159
|
+
child.on('error', () => resolve({ code: null, out: '' }));
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
resolve({ code: null, out: '' });
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
const v = await run(['--version']);
|
|
166
|
+
const version = /(\d+\.\d+\.\d+[\w.-]*)/.exec(v.out)?.[1] ?? null;
|
|
167
|
+
const st = await run(['login', 'status']);
|
|
168
|
+
const loggedIn = st.code === 0 ? !/not logged in/i.test(st.out) : st.code === null ? null : false;
|
|
169
|
+
return { state: 'present', version, loggedIn };
|
|
170
|
+
}
|
|
131
171
|
/** Default writable check: create the directory, write and remove a temp file. Returns an error message or null. */
|
|
132
172
|
export function writableProbe(dir) {
|
|
133
173
|
try {
|
|
@@ -149,6 +189,8 @@ export function defaultDeps(env = process.env, isTTY = process.stdout.isTTY ===
|
|
|
149
189
|
nodeVersion: process.versions.node,
|
|
150
190
|
platform: process.platform,
|
|
151
191
|
claude: () => claudeProbe(),
|
|
192
|
+
codex: () => codexProbe(4000, env),
|
|
193
|
+
analystEngine: env.DEXTERM_ANALYST_ENGINE ?? loadConfig(dextermHome(env)).config.analyst?.engine ?? null,
|
|
152
194
|
probe: (url) => livingProbe(url),
|
|
153
195
|
checkWritable: writableProbe,
|
|
154
196
|
offline: env.DEXTERM_OFFLINE === '1' || env.DEXTERM_OFFLINE === 'true',
|
|
@@ -217,11 +259,20 @@ export async function runDoctor(deps) {
|
|
|
217
259
|
}
|
|
218
260
|
// 3. Claude CLI (the analyst). Not required to browse; required for the `a` analyst.
|
|
219
261
|
const claude = await deps.claude();
|
|
220
|
-
|
|
262
|
+
// 0.1.5: codex is the optional second engine; a missing claude is fine when codex can run the analyst.
|
|
263
|
+
const codex = deps.codex ? await deps.codex() : null;
|
|
264
|
+
const codexUsable = codex?.state === 'present';
|
|
265
|
+
if (claude === 'missing' && codexUsable) {
|
|
266
|
+
checks.push({
|
|
267
|
+
name: 'Claude CLI', status: 'pass',
|
|
268
|
+
detail: 'not found on PATH (optional now): the analyst runs on your codex CLI instead',
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
else if (claude === 'missing') {
|
|
221
272
|
checks.push({
|
|
222
273
|
name: 'Claude CLI', status: 'warn',
|
|
223
274
|
detail: 'not found on PATH; the analyst (a) is disabled, the rest of dexterm works',
|
|
224
|
-
fix: 'Install Claude Code (https://claude.com/claude-code) and run `claude` once to log in
|
|
275
|
+
fix: 'Install Claude Code (https://claude.com/claude-code) and run `claude` once to log in, or use Codex: `npm i -g @openai/codex && codex login`.',
|
|
225
276
|
});
|
|
226
277
|
}
|
|
227
278
|
else {
|
|
@@ -230,6 +281,24 @@ export async function runDoctor(deps) {
|
|
|
230
281
|
detail: 'found on PATH (the analyst runs your local `claude`; log in once with `claude` if the analyst errors)',
|
|
231
282
|
});
|
|
232
283
|
}
|
|
284
|
+
// 3b. Codex CLI (0.1.5): optional second analyst engine, the user's own codex login. Read-only lock needs >= CODEX_MIN_VERSION.
|
|
285
|
+
if (codex) {
|
|
286
|
+
const choice = parseEngineChoice(deps.analystEngine) ?? 'auto';
|
|
287
|
+
const engine = resolveEngine(choice, (b) => b === 'claude' ? claude !== 'missing' : codexUsable);
|
|
288
|
+
const eng = `analyst engine: ${engine}${choice === 'auto' ? ' (auto)' : ''}`;
|
|
289
|
+
if (codex.state === 'missing') {
|
|
290
|
+
checks.push({ name: 'Codex CLI', status: engine === 'codex' ? 'warn' : 'pass', detail: `not installed (optional second analyst engine) · ${eng}`, ...(engine === 'codex' ? { fix: 'npm i -g @openai/codex && codex login, or set analyst.engine to claude' } : {}) });
|
|
291
|
+
}
|
|
292
|
+
else if (codex.version && !versionAtLeast(codex.version, CODEX_MIN_VERSION)) {
|
|
293
|
+
checks.push({ name: 'Codex CLI', status: 'warn', detail: `codex-cli ${codex.version} is older than ${CODEX_MIN_VERSION}; the read-only analyst lock needs ${CODEX_MIN_VERSION}+ · ${eng}`, fix: 'npm i -g @openai/codex@latest' });
|
|
294
|
+
}
|
|
295
|
+
else if (codex.loggedIn === false) {
|
|
296
|
+
checks.push({ name: 'Codex CLI', status: 'warn', detail: `found${codex.version ? ` (codex-cli ${codex.version})` : ''}, not logged in · ${eng}`, fix: 'codex login' });
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
checks.push({ name: 'Codex CLI', status: 'pass', detail: `found${codex.version ? ` (codex-cli ${codex.version})` : ''}${codex.loggedIn ? ', logged in' : ''}; runs the analyst read-only on your own codex login (/engine codex) · ${eng}` });
|
|
300
|
+
}
|
|
301
|
+
}
|
|
233
302
|
// 4b. Login with X (optional, task-1357): the user's own X app + login unlock X posts in the SOCIAL panel / TRENDING.
|
|
234
303
|
{
|
|
235
304
|
const xs = new XTokenStore({ env: deps.env });
|
|
@@ -237,8 +306,10 @@ export async function runDoctor(deps) {
|
|
|
237
306
|
const modes = xs.modes();
|
|
238
307
|
const clientId = xs.clientId(deps.env);
|
|
239
308
|
if (meta.loggedIn) {
|
|
309
|
+
const windows = deps.platform === 'win32';
|
|
240
310
|
const loose = (modes.tokens !== null && modes.tokens !== 0o600) || (modes.dir !== null && modes.dir !== 0o700);
|
|
241
|
-
|
|
311
|
+
const warning = windows ? ' · Windows ACL isolation is not verified' : loose ? ' · token file permissions are looser than 0600' : '';
|
|
312
|
+
checks.push({ name: 'Login with X', status: windows || loose ? 'warn' : 'pass', detail: `logged in${meta.user ? ` as @${meta.user.username}` : ''} · scopes ${meta.scopes.join(' ') || '?'} · refresh ${meta.hasRefresh ? 'yes' : 'no'}${warning}`, ...(windows ? { fix: 'Review the token directory Security permissions in Windows; chmod does not enforce owner-only ACLs.' } : loose ? { fix: `chmod 700 ${xs.dir} && chmod 600 ${xs.tokensPath}` } : {}) });
|
|
242
313
|
}
|
|
243
314
|
else if (clientId) {
|
|
244
315
|
checks.push({ name: 'Login with X', status: 'warn', detail: 'client id stored, not logged in — X posts stay off (keyless venues, news and on-chain rows still work)', fix: 'dexterm x login' });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// dexterm — library surface of the CLI package (used by tests and tools).
|
|
2
2
|
export * from "./state.js";
|
|
3
3
|
export * from "./fixtures.js";
|
|
4
|
-
export { parseArgs, stateFromArgs, applyScreenerArgs, applyEffectArgs, applyAction, applyAnalystAction, applyKey, colorDepthFor, intensityFor, asciiRainFor, fpsFor, demoEvent, main, interactive, terminalGuard, describeError, buildScreenState, HELP, LIVE_REFRESH_MS, FPS_BY_INTENSITY, MAX_FPS, DEMO_KEYS, } from "./main.js";
|
|
4
|
+
export { parseArgs, stateFromArgs, applyScreenerArgs, applyEffectArgs, applyAction, applyAnalystAction, applyFollowNav, applyKey, colorDepthFor, intensityFor, asciiRainFor, fpsFor, demoEvent, main, interactive, terminalGuard, describeError, buildScreenState, HELP, LIVE_REFRESH_MS, FPS_BY_INTENSITY, MAX_FPS, DEMO_KEYS, } from "./main.js";
|
|
5
5
|
export { AnalystSession, parseResults, parseFilterApplied } from "./analyst/session.js";
|
|
6
6
|
export { resolveAnalystResults } from "./analyst/results.js";
|
|
7
7
|
export { runAnalyst, buildArgv, turnPrompt, analystContextBlock, untrusted, ASSETS as ANALYST_ASSETS, MCP_BIN } from "./analyst/backend.js";
|
|
@@ -66,6 +66,9 @@ function openEditor(io, file) {
|
|
|
66
66
|
const ed = (io.env.VISUAL || io.env.EDITOR || '').trim();
|
|
67
67
|
if (!ed || io.isTTY === false)
|
|
68
68
|
return 'no-editor';
|
|
69
|
+
// task-1423: Windows has no /bin/sh, so the spawn below always failed there; print the path to edit instead
|
|
70
|
+
if ((io.platform ?? process.platform) === 'win32')
|
|
71
|
+
return 'no-editor';
|
|
69
72
|
// `sh -c '$EDITOR "$1"'` so an EDITOR with arguments ("code -w") works; the path is passed as $1, never interpolated.
|
|
70
73
|
const r = spawnSync('/bin/sh', ['-c', `${ed} "$1"`, 'sh', file], { stdio: 'inherit', env: io.env });
|
|
71
74
|
return r.status === 0 ? 'edited' : 'failed';
|