diff --git a/.prettierrc b/.prettierrc
index 62bcd749fe..9cce43c86b 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -8,5 +8,13 @@
"printWidth": 100,
"bracketSpacing": true,
"arrowParens": "always",
- "endOfLine": "lf"
+ "endOfLine": "lf",
+ "overrides": [
+ {
+ "files": "docs/**/*.mdx",
+ "options": {
+ "parser": "mdx"
+ }
+ }
+ ]
}
diff --git a/docs/agentic-git-identity.md b/docs/agentic-git-identity.mdx
similarity index 87%
rename from docs/agentic-git-identity.md
rename to docs/agentic-git-identity.mdx
index 48cccf1d2f..69405ee8a8 100644
--- a/docs/agentic-git-identity.md
+++ b/docs/agentic-git-identity.mdx
@@ -26,7 +26,9 @@ Create a separate GitHub account for your agent:
2. Use a distinctive username (e.g., `yourname-agent`, `yourname-ai`)
3. Use a separate email (GitHub allows plus-addressing: `yourname+ai@example.com`)
-This is optional but recommended. You can also use your main account with a different email/name.
+
+ This is optional but recommended. You can also use your main account with a different email/name.
+
## Step 2: Generate Classic GitHub Token
@@ -57,7 +59,10 @@ Add the Git identity environment variables as [Project Secrets](/project-secrets
These environment variables will be automatically injected when the agent runs Git commands in that project.
-If you need the agent identity outside of mux, you can alternatively set these as global environment variables in your shell configuration (`~/.zshrc`, `~/.bashrc`, etc.)
+
+ If you need the agent identity outside of mux, you can alternatively set these as global
+ environment variables in your shell configuration (`~/.zshrc`, `~/.bashrc`, etc.)
+
## Step 4: Configure GitHub Authentication
@@ -101,4 +106,7 @@ git config --global credential.helper ""
git config --global --add credential.helper '!gh auth git-credential'
```
-The "replace all" approach will disable platform keychain helpers and may break Git authentication for non-GitHub remotes (GitLab, Bitbucket, etc.).
+
+ The "replace all" approach will disable platform keychain helpers and may break Git authentication
+ for non-GitHub remotes (GitLab, Bitbucket, etc.).
+
diff --git a/docs/benchmarking.md b/docs/benchmarking.mdx
similarity index 100%
rename from docs/benchmarking.md
rename to docs/benchmarking.mdx
diff --git a/docs/cli.md b/docs/cli.mdx
similarity index 100%
rename from docs/cli.md
rename to docs/cli.mdx
diff --git a/docs/context-management.md b/docs/context-management.mdx
similarity index 97%
rename from docs/context-management.md
rename to docs/context-management.mdx
index 5dd6957afa..f5ff66ed4e 100644
--- a/docs/context-management.md
+++ b/docs/context-management.mdx
@@ -152,7 +152,9 @@ Remove oldest 50% of messages.
### OpenAI Responses API Limitation
-`/truncate` does not work with OpenAI models due to the Responses API architecture:
+
+ `/truncate` does not work with OpenAI models due to the Responses API architecture:
+
- OpenAI's Responses API stores conversation state server-side
- Manual message deletion via `/truncate` doesn't affect the server-side state
diff --git a/docs/fork.md b/docs/fork.mdx
similarity index 100%
rename from docs/fork.md
rename to docs/fork.mdx
diff --git a/docs/index.md b/docs/index.mdx
similarity index 100%
rename from docs/index.md
rename to docs/index.mdx
diff --git a/docs/init-hooks.md b/docs/init-hooks.mdx
similarity index 100%
rename from docs/init-hooks.md
rename to docs/init-hooks.mdx
diff --git a/docs/install.md b/docs/install.mdx
similarity index 79%
rename from docs/install.md
rename to docs/install.mdx
index 45a6e582f6..c09d91192e 100644
--- a/docs/install.md
+++ b/docs/install.mdx
@@ -22,7 +22,10 @@ Download pre-built binaries of `main` from [GitHub Actions](https://github.com/c
- `macos-dmg-arm64` (Apple Silicon)
- **Linux**: AppImage (portable, works on most distros)
-Windows builds are only available from [releases](https://github.com/coder/mux/releases), not from development builds.
+
+ Windows builds are only available from [releases](https://github.com/coder/mux/releases), not from
+ development builds.
+
To download:
@@ -57,11 +60,17 @@ The app is code-signed and notarized by Apple, so it will open without security
3. Follow the installation prompts
4. Launch Mux from the Start menu or desktop shortcut
-Windows support is currently in alpha. Please [report any issues](https://github.com/coder/mux/issues) you encounter.
+
+ Windows support is currently in alpha. Please [report any
+ issues](https://github.com/coder/mux/issues) you encounter.
+
### Testing Pre-Release Builds
-Only builds from the `main` branch are signed and notarized. If you're testing a build from a pull request or other branch, you'll need to bypass macOS Gatekeeper:
+
+ Only builds from the `main` branch are signed and notarized. If you're testing a build from a pull
+ request or other branch, you'll need to bypass macOS Gatekeeper:
+
1. After installing, open Terminal
2. Run: `xattr -cr /Applications/Mux.app`
diff --git a/docs/instruction-files.md b/docs/instruction-files.mdx
similarity index 100%
rename from docs/instruction-files.md
rename to docs/instruction-files.mdx
diff --git a/docs/keybinds.md b/docs/keybinds.mdx
similarity index 95%
rename from docs/keybinds.md
rename to docs/keybinds.mdx
index 3d97ee43a9..7d98ea54c4 100644
--- a/docs/keybinds.md
+++ b/docs/keybinds.mdx
@@ -5,7 +5,10 @@ description: Complete keyboard shortcut reference for mux
mux is designed to be keyboard-driven for maximum efficiency. All major actions have keyboard shortcuts.
-This document should be kept in sync with `src/utils/ui/keybinds.ts`, which is the source of truth for keybind definitions.
+
+ This document should be kept in sync with `src/utils/ui/keybinds.ts`, which is the source of truth
+ for keybind definitions.
+
## Platform Conventions
diff --git a/docs/models.md b/docs/models.mdx
similarity index 84%
rename from docs/models.md
rename to docs/models.mdx
index 88a3011dc4..e5d9ded20f 100644
--- a/docs/models.md
+++ b/docs/models.mdx
@@ -9,6 +9,28 @@ See also:
mux supports multiple AI providers through its flexible provider architecture.
+### First-class models
+
+mux ships with a curated set of first-class models that we keep up to date with the frontier. You can also use any custom model from a supported provider with `/model `.
+
+{/* BEGIN KNOWN_MODELS_TABLE */}
+
+| Model | ID | Aliases | Default |
+| -------------------- | --------------------------- | ---------------------------------------- | ------- |
+| Opus 4.5 | anthropic:claude-opus-4-5 | `opus` | ✓ |
+| Sonnet 4.5 | anthropic:claude-sonnet-4-5 | `sonnet` | |
+| Haiku 4.5 | anthropic:claude-haiku-4-5 | `haiku` | |
+| GPT-5.1 | openai:gpt-5.1 | `gpt-5.1` | |
+| GPT-5 Pro | openai:gpt-5-pro | `gpt-5-pro` | |
+| GPT-5.1 Codex | openai:gpt-5.1-codex | `codex` | |
+| GPT-5.1 Codex Mini | openai:gpt-5.1-codex-mini | `codex-mini` | |
+| GPT-5.1 Codex Max | openai:gpt-5.1-codex-max | `codex-max` | |
+| Gemini 3 Pro Preview | google:gemini-3-pro-preview | `gemini-3`, `gemini-3-pro` | |
+| Grok 4 1 Fast | xai:grok-4-1-fast | `grok`, `grok-4`, `grok-4.1`, `grok-4-1` | |
+| Grok Code Fast 1 | xai:grok-code-fast-1 | `grok-code` | |
+
+{/* END KNOWN_MODELS_TABLE */}
+
### Supported Providers
#### Anthropic (Cloud)
diff --git a/docs/mux-codes.md b/docs/mux-codes.mdx
similarity index 100%
rename from docs/mux-codes.md
rename to docs/mux-codes.mdx
diff --git a/docs/project-secrets.md b/docs/project-secrets.mdx
similarity index 100%
rename from docs/project-secrets.md
rename to docs/project-secrets.mdx
diff --git a/docs/prompting-tips.md b/docs/prompting-tips.mdx
similarity index 100%
rename from docs/prompting-tips.md
rename to docs/prompting-tips.mdx
diff --git a/docs/runtime.md b/docs/runtime.mdx
similarity index 100%
rename from docs/runtime.md
rename to docs/runtime.mdx
diff --git a/docs/runtime/local.md b/docs/runtime/local.mdx
similarity index 62%
rename from docs/runtime/local.md
rename to docs/runtime/local.mdx
index 0c43094a5f..7670b21f69 100644
--- a/docs/runtime/local.md
+++ b/docs/runtime/local.mdx
@@ -13,9 +13,15 @@ Local runtime runs the agent directly in your project directory—the same direc
## Caveats
-**No isolation**: Multiple local workspaces for the same project see and modify the same files. Running them simultaneously can cause conflicts. mux shows a warning when another local workspace is actively streaming.
-
-**Affects your working copy**: Agent changes happen in your actual project directory.
+
+ **No isolation**: Multiple local workspaces for the same project see and modify the same files.
+ Running them simultaneously can cause conflicts. mux shows a warning when another local workspace
+ is actively streaming.
+
+
+
+ **Affects your working copy**: Agent changes happen in your actual project directory.
+
## Filesystem
diff --git a/docs/runtime/ssh.md b/docs/runtime/ssh.mdx
similarity index 95%
rename from docs/runtime/ssh.md
rename to docs/runtime/ssh.mdx
index 2a8277733d..094eb354f6 100644
--- a/docs/runtime/ssh.md
+++ b/docs/runtime/ssh.mdx
@@ -36,7 +36,9 @@ Host ovh-1
## Authentication
-As we delegate to `ssh`, this is really an abbreviated reference of how `ssh` authenticates.
+
+ As we delegate to `ssh`, this is really an abbreviated reference of how `ssh` authenticates.
+
There are a few practical ways to set up authentication.
diff --git a/docs/runtime/worktree.md b/docs/runtime/worktree.mdx
similarity index 100%
rename from docs/runtime/worktree.md
rename to docs/runtime/worktree.mdx
diff --git a/docs/storybook.md b/docs/storybook.mdx
similarity index 100%
rename from docs/storybook.md
rename to docs/storybook.mdx
diff --git a/docs/system-prompt.md b/docs/system-prompt.mdx
similarity index 99%
rename from docs/system-prompt.md
rename to docs/system-prompt.mdx
index 3b7de1ba03..9a794dbe39 100644
--- a/docs/system-prompt.md
+++ b/docs/system-prompt.mdx
@@ -13,7 +13,6 @@ Even with consistent support at the protocol layer, we have found that different
Here's a snippet from `src/node/services/systemMessage.ts` which is our shared system prompt (minus tools).
-
{/* BEGIN SYSTEM_PROMPT_DOCS */}
```typescript
diff --git a/docs/telemetry.md b/docs/telemetry.mdx
similarity index 100%
rename from docs/telemetry.md
rename to docs/telemetry.mdx
diff --git a/docs/vim-mode.md b/docs/vim-mode.mdx
similarity index 100%
rename from docs/vim-mode.md
rename to docs/vim-mode.mdx
diff --git a/docs/vscode-extension.md b/docs/vscode-extension.mdx
similarity index 100%
rename from docs/vscode-extension.md
rename to docs/vscode-extension.mdx
diff --git a/docs/why-parallelize.md b/docs/why-parallelize.mdx
similarity index 100%
rename from docs/why-parallelize.md
rename to docs/why-parallelize.mdx
diff --git a/docs/workspaces.md b/docs/workspaces.mdx
similarity index 100%
rename from docs/workspaces.md
rename to docs/workspaces.mdx
diff --git a/fmt.mk b/fmt.mk
index 87339f896c..15bd24a3ce 100644
--- a/fmt.mk
+++ b/fmt.mk
@@ -6,7 +6,7 @@
.PHONY: fmt fmt-check fmt-prettier fmt-prettier-check fmt-shell fmt-shell-check fmt-nix fmt-nix-check fmt-python fmt-python-check fmt-sync-docs fmt-sync-docs-check
# Centralized patterns - single source of truth
-PRETTIER_PATTERNS := 'src/**/*.{ts,tsx,json}' 'mobile/**/*.{ts,tsx,json}' 'tests/**/*.ts' 'docs/**/*.md' 'package.json' 'tsconfig*.json' 'README.md'
+PRETTIER_PATTERNS := 'src/**/*.{ts,tsx,json}' 'mobile/**/*.{ts,tsx,json}' 'tests/**/*.ts' 'docs/**/*.mdx' 'package.json' 'tsconfig*.json' 'README.md'
SHELL_SCRIPTS := scripts
PYTHON_DIRS := benchmarks
@@ -94,7 +94,7 @@ else
endif
fmt-sync-docs:
- @./scripts/sync_system_prompt_docs.sh
+ @bun scripts/gen_docs.ts
fmt-sync-docs-check:
- @./scripts/sync_system_prompt_docs.sh check
+ @bun scripts/gen_docs.ts check
diff --git a/scripts/gen_docs.ts b/scripts/gen_docs.ts
new file mode 100644
index 0000000000..d7e4a967ca
--- /dev/null
+++ b/scripts/gen_docs.ts
@@ -0,0 +1,185 @@
+#!/usr/bin/env bun
+/**
+ * Generate documentation snippets from source files.
+ *
+ * Usage:
+ * bun scripts/gen_docs.ts # write mode (update docs)
+ * bun scripts/gen_docs.ts check # check mode (verify docs are up-to-date)
+ *
+ * This script synchronizes:
+ * - docs/system-prompt.mdx: snippet from src/node/services/systemMessage.ts
+ * - docs/models.mdx: table from src/common/constants/knownModels.ts
+ */
+
+import * as fs from "fs";
+import * as path from "path";
+import { KNOWN_MODELS, DEFAULT_MODEL } from "../src/common/constants/knownModels";
+import { formatModelDisplayName } from "../src/common/utils/ai/modelDisplay";
+
+const MODE = process.argv[2] === "check" ? "check" : "write";
+const DOCS_DIR = path.join(import.meta.dir, "..", "docs");
+
+// ---------------------------------------------------------------------------
+// Marker helpers
+// ---------------------------------------------------------------------------
+
+function injectBetweenMarkers(content: string, markerName: string, block: string): string {
+ const beginMarker = `{/* BEGIN ${markerName} */}`;
+ const endMarker = `{/* END ${markerName} */}`;
+
+ const beginIdx = content.indexOf(beginMarker);
+ const endIdx = content.indexOf(endMarker);
+
+ if (beginIdx === -1 || endIdx === -1) {
+ throw new Error(`Missing markers for ${markerName}`);
+ }
+
+ const before = content.slice(0, beginIdx + beginMarker.length);
+ const after = content.slice(endIdx);
+
+ return `${before}\n\n${block}\n\n${after}`;
+}
+
+// ---------------------------------------------------------------------------
+// Generic sync helper
+// ---------------------------------------------------------------------------
+
+interface SyncDocOptions {
+ docsFile: string;
+ sourceLabel: string;
+ markerName: string;
+ generateBlock: () => string;
+}
+
+async function syncDoc(options: SyncDocOptions): Promise {
+ const { docsFile, sourceLabel, markerName, generateBlock } = options;
+ const docsPath = path.join(DOCS_DIR, docsFile);
+
+ const currentContent = fs.readFileSync(docsPath, "utf-8");
+ const block = generateBlock();
+ const newContent = injectBetweenMarkers(currentContent, markerName, block);
+
+ if (currentContent === newContent) {
+ console.log(`✓ ${docsFile} is up-to-date with ${sourceLabel}`);
+ return true;
+ }
+
+ if (MODE === "check") {
+ console.error(`✗ ${docsFile} is out of sync with ${sourceLabel}`);
+ console.error(` Run 'make fmt' to regenerate.`);
+ return false;
+ }
+
+ fs.writeFileSync(docsPath, newContent, "utf-8");
+ console.log(`✓ Updated ${docsFile} from ${sourceLabel}`);
+ return true;
+}
+
+// ---------------------------------------------------------------------------
+// System prompt sync
+// ---------------------------------------------------------------------------
+
+function generateSystemPromptBlock(): string {
+ const systemMessagePath = path.join(
+ import.meta.dir,
+ "..",
+ "src",
+ "node",
+ "services",
+ "systemMessage.ts"
+ );
+ const source = fs.readFileSync(systemMessagePath, "utf-8");
+
+ const regionStart = "// #region SYSTEM_PROMPT_DOCS";
+ const regionEnd = "// #endregion SYSTEM_PROMPT_DOCS";
+
+ const startIdx = source.indexOf(regionStart);
+ const endIdx = source.indexOf(regionEnd);
+
+ if (startIdx === -1 || endIdx === -1) {
+ throw new Error("Could not find SYSTEM_PROMPT_DOCS region in systemMessage.ts");
+ }
+
+ const snippet = source.slice(startIdx + regionStart.length, endIdx).trim();
+ return "```typescript\n" + snippet + "\n```";
+}
+
+async function syncSystemPrompt(): Promise {
+ return syncDoc({
+ docsFile: "system-prompt.mdx",
+ sourceLabel: "src/node/services/systemMessage.ts",
+ markerName: "SYSTEM_PROMPT_DOCS",
+ generateBlock: generateSystemPromptBlock,
+ });
+}
+
+// ---------------------------------------------------------------------------
+// Known models table sync
+// ---------------------------------------------------------------------------
+
+function generateKnownModelsTable(): string {
+ const rows: Array<{ name: string; id: string; aliases: string; isDefault: boolean }> = [];
+
+ for (const model of Object.values(KNOWN_MODELS)) {
+ rows.push({
+ name: formatModelDisplayName(model.providerModelId),
+ id: model.id,
+ aliases: (model.aliases ?? []).map((a) => `\`${a}\``).join(", ") || "—",
+ isDefault: model.id === DEFAULT_MODEL,
+ });
+ }
+
+ // Calculate column widths
+ const headers = ["Model", "ID", "Aliases", "Default"];
+ const widths = headers.map((h, i) => {
+ const colValues = rows.map((r) => {
+ if (i === 0) return r.name;
+ if (i === 1) return r.id;
+ if (i === 2) return r.aliases;
+ return r.isDefault ? "✓" : "";
+ });
+ return Math.max(h.length, ...colValues.map((v) => v.length));
+ });
+
+ const pad = (s: string, w: number) => s + " ".repeat(w - s.length);
+
+ const headerRow = `| ${headers.map((h, i) => pad(h, widths[i])).join(" | ")} |`;
+ const sepRow = `| ${widths.map((w) => "-".repeat(w)).join(" | ")} |`;
+ const dataRows = rows.map((r) => {
+ const cells = [
+ pad(r.name, widths[0]),
+ pad(r.id, widths[1]),
+ pad(r.aliases, widths[2]),
+ pad(r.isDefault ? "✓" : "", widths[3]),
+ ];
+ return `| ${cells.join(" | ")} |`;
+ });
+
+ return [headerRow, sepRow, ...dataRows].join("\n");
+}
+
+async function syncKnownModels(): Promise {
+ return syncDoc({
+ docsFile: "models.mdx",
+ sourceLabel: "src/common/constants/knownModels.ts",
+ markerName: "KNOWN_MODELS_TABLE",
+ generateBlock: generateKnownModelsTable,
+ });
+}
+
+// ---------------------------------------------------------------------------
+// Main
+// ---------------------------------------------------------------------------
+
+async function main(): Promise {
+ const results = await Promise.all([syncSystemPrompt(), syncKnownModels()]);
+
+ if (results.some((r) => !r)) {
+ process.exit(1);
+ }
+}
+
+main().catch((err) => {
+ console.error(err);
+ process.exit(1);
+});
diff --git a/scripts/sync_system_prompt_docs.sh b/scripts/sync_system_prompt_docs.sh
deleted file mode 100755
index 97069eeeb7..0000000000
--- a/scripts/sync_system_prompt_docs.sh
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/usr/bin/env bash
-# Syncs the code block in docs/system-prompt.md with src/node/services/systemMessage.ts
-# Usage:
-# ./scripts/sync_system_prompt_docs.sh # Update docs
-# ./scripts/sync_system_prompt_docs.sh check # Check if in sync (exit 1 if not)
-
-set -euo pipefail
-
-SOURCE_FILE="src/node/services/systemMessage.ts"
-DOCS_FILE="docs/system-prompt.md"
-
-# Extract code between #region and #endregion SYSTEM_PROMPT_DOCS markers
-extract_code_block() {
- sed -n '/^\/\/ #region SYSTEM_PROMPT_DOCS$/,/^\/\/ #endregion SYSTEM_PROMPT_DOCS$/p' "$SOURCE_FILE" \
- | sed '1d;$d' # Remove the marker lines themselves
-}
-
-# Generate the synced section (code block only)
-generate_section() {
- echo '```typescript'
- extract_code_block
- echo '```'
-}
-
-# Extract the current synced section from docs
-# Uses JSX-style comments for Mintlify MDX compatibility
-extract_current_section() {
- sed -n '/{\/\* BEGIN SYSTEM_PROMPT_DOCS \*\/}/,/{\/\* END SYSTEM_PROMPT_DOCS \*\/}/p' "$DOCS_FILE" \
- | tail -n +2 | head -n -1 \
- |
- # Remove first and last lines (markers)
- sed '1{/^$/d}' | sed '${/^$/d}' # Trim leading/trailing blank lines
-}
-
-if [[ "${1:-}" == "check" ]]; then
- generated=$(generate_section)
- current=$(extract_current_section)
-
- if [[ "$generated" != "$current" ]]; then
- echo "❌ $DOCS_FILE is out of sync with $SOURCE_FILE"
- echo "Run 'make fmt' to update."
- exit 1
- fi
- echo "✅ $DOCS_FILE is in sync"
-else
- # Replace section between markers using temp file approach
- # Uses JSX-style comments for Mintlify MDX compatibility
- {
- # Print everything up to and including BEGIN marker
- sed -n '1,/{\/\* BEGIN SYSTEM_PROMPT_DOCS \*\/}/p' "$DOCS_FILE"
- echo ""
- generate_section
- echo ""
- # Print END marker and everything after
- sed -n '/{\/\* END SYSTEM_PROMPT_DOCS \*\/}/,$p' "$DOCS_FILE"
- } >"$DOCS_FILE.tmp"
- mv "$DOCS_FILE.tmp" "$DOCS_FILE"
- echo "Updated $DOCS_FILE"
-fi