diff --git a/docs/AGENTS.md b/docs/AGENTS.md index 9abdccd372..711becab2f 100644 --- a/docs/AGENTS.md +++ b/docs/AGENTS.md @@ -36,7 +36,7 @@ gh pr view --json mergeable,mergeStateStatus | jq '.' ## Documentation Rules -- No free-floating Markdown. User docs live in `docs/` (read `docs/README.md`, add pages to `docs.json` navigation, use standard Markdown + mermaid). Developer/test notes belong inline as comments. +- No free-floating Markdown. User docs live in `docs/` (read `docs/README.mdx`, add pages to `docs.json` navigation, use standard Markdown + mermaid). Developer/test notes belong inline as comments. - For planning artifacts, use the `propose_plan` tool or inline comments instead of ad-hoc docs. - Do not add new root-level docs without explicit request; during feature work rely on code + tests + inline comments. - Test documentation stays inside the relevant test file as commentary explaining setup/edge cases. @@ -168,6 +168,10 @@ Avoid mock-heavy tests that verify implementation details rather than behavior. - When Plan Mode is requested, assume the user wants the actual completed plan; do not merely describe how you would devise one. - Attach a net LoC estimate (product code only) to each recommended approach. +## Model: openai.\* + +- Use the `gh` CLI for GitHub operations (e.g., opening/submitting PRs). + ## Tool: status_set - Set status url to the Pull Request once opened diff --git a/docs/README.md b/docs/README.md index 05ad3c3dfb..99a49f2888 100644 --- a/docs/README.md +++ b/docs/README.md @@ -24,12 +24,12 @@ docs/ ├── docs.json # Mintlify configuration (navigation, theme, etc.) ├── custom.css # Custom styling ├── img/ # Images and logos -└── *.md # Documentation pages +└── *.mdx # Documentation pages ``` ## Adding Content -1. Create a new `.md` file in `docs/` +1. Create a new `.mdx` file in `docs/` 2. Add frontmatter with title and description 3. Add the page to `docs.json` navigation 4. Use standard markdown + mermaid diagrams @@ -45,7 +45,7 @@ description: Brief description for SEO ## Writing Guidelines -See [STYLE.md](./STYLE.md) for documentation writing guidelines. +See [STYLE.mdx](./STYLE.mdx) for documentation writing guidelines. ## CI/CD diff --git a/docs/agentic-git-identity.md b/docs/agentic-git-identity.md index 48cccf1d2f..69405ee8a8 100644 --- a/docs/agentic-git-identity.md +++ b/docs/agentic-git-identity.md @@ -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/context-management.md b/docs/context-management.md index 5dd6957afa..f5ff66ed4e 100644 --- a/docs/context-management.md +++ b/docs/context-management.md @@ -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/install.md b/docs/install.md index 45a6e582f6..c09d91192e 100644 --- a/docs/install.md +++ b/docs/install.md @@ -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/keybinds.md b/docs/keybinds.md index 3d97ee43a9..7d98ea54c4 100644 --- a/docs/keybinds.md +++ b/docs/keybinds.md @@ -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..5af5667304 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` | Yes | +| 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/runtime/local.md b/docs/runtime/local.md index 0c43094a5f..7670b21f69 100644 --- a/docs/runtime/local.md +++ b/docs/runtime/local.md @@ -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.md index 2a8277733d..094eb354f6 100644 --- a/docs/runtime/ssh.md +++ b/docs/runtime/ssh.md @@ -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/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/fmt.mk b/fmt.mk index 87339f896c..c30e7413f5 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/**/*.{md,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..5440352801 --- /dev/null +++ b/scripts/gen_docs.ts @@ -0,0 +1,129 @@ +#!/usr/bin/env bun + +// Generates and checks doc snippets so make fmt updates them and make fmt-check flags drift. + +import fs from "node:fs/promises"; +import path from "node:path"; + +import { DEFAULT_MODEL, KNOWN_MODELS } from "../src/common/constants/knownModels"; +import { formatModelDisplayName } from "../src/common/utils/ai/modelDisplay"; + +const MODE: "write" | "check" = process.argv[2] === "check" ? "check" : "write"; + +interface SyncResult { + changed: boolean; + message: string; +} + +interface SyncConfig { + docsFile: string; + sourceLabel: string; + markerName: string; + generateBlock: () => Promise | string; +} + +async function syncDoc(config: SyncConfig): Promise { + const beginMarker = `{/* BEGIN ${config.markerName} */}`; + const endMarker = `{/* END ${config.markerName} */}`; + const docsPath = path.join(process.cwd(), config.docsFile); + const relPath = path.relative(process.cwd(), docsPath); + + const block = await config.generateBlock(); + const original = await fs.readFile(docsPath, "utf8"); + const updated = injectBetweenMarkers(original, beginMarker, endMarker, block, docsPath); + const changed = original !== updated; + + if (MODE === "check") { + return { + changed, + message: changed + ? `❌ ${relPath} is out of sync with ${config.sourceLabel}` + : `✅ ${relPath} is in sync`, + }; + } + + if (changed) { + await fs.writeFile(docsPath, updated); + return { changed: true, message: `Updated ${relPath}` }; + } + return { changed: false, message: `${relPath} already up to date` }; +} + +async function main() { + const results = await Promise.all([ + syncDoc({ + docsFile: "docs/system-prompt.mdx", + sourceLabel: "src/node/services/systemMessage.ts", + markerName: "SYSTEM_PROMPT_DOCS", + generateBlock: generateSystemPromptBlock, + }), + syncDoc({ + docsFile: "docs/models.mdx", + sourceLabel: "src/common/constants/knownModels.ts", + markerName: "KNOWN_MODELS_TABLE", + generateBlock: generateKnownModelsTable, + }), + ]); + + results.forEach((r) => console.log(r.message)); + if (MODE === "check" && results.some((r) => r.changed)) { + process.exit(1); + } +} + +async function generateSystemPromptBlock(): Promise { + const sourcePath = path.join(process.cwd(), "src/node/services/systemMessage.ts"); + const source = await fs.readFile(sourcePath, "utf8"); + const regionMatch = source.match( + /\/\/ #region SYSTEM_PROMPT_DOCS\r?\n([\s\S]*?)\r?\n\/\/ #endregion SYSTEM_PROMPT_DOCS/ + ); + if (!regionMatch) { + throw new Error("SYSTEM_PROMPT_DOCS markers not found in src/node/services/systemMessage.ts"); + } + return ["```typescript", regionMatch[1].trimEnd(), "```"].join("\n"); +} + +function generateKnownModelsTable(): string { + const rows = Object.values(KNOWN_MODELS).map((model) => ({ + name: formatModelDisplayName(model.providerModelId), + id: `\`${model.id}\``, + aliases: model.aliases?.length ? model.aliases.map((a) => `\`${a}\``).join(", ") : "—", + isDefault: model.id === DEFAULT_MODEL ? "Yes" : "—", + })); + + const headers = ["Model", "ID", "Aliases", "Default"]; + const keys: (keyof (typeof rows)[0])[] = ["name", "id", "aliases", "isDefault"]; + const colWidths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => r[keys[i]].length))); + + const pad = (s: string, w: number) => s + " ".repeat(w - s.length); + const headerRow = `| ${headers.map((h, i) => pad(h, colWidths[i])).join(" | ")} |`; + const sepRow = `| ${colWidths.map((w) => "-".repeat(w)).join(" | ")} |`; + const dataRows = rows.map((r) => `| ${keys.map((k, i) => pad(r[k], colWidths[i])).join(" | ")} |`); + + return [headerRow, sepRow, ...dataRows].join("\n"); +} + +function injectBetweenMarkers( + doc: string, + begin: string, + end: string, + block: string, + filePath: string +): string { + const start = doc.indexOf(begin); + const finish = doc.indexOf(end); + + if (start === -1 || finish === -1) { + throw new Error(`Markers ${begin} and ${end} must exist in ${filePath}`); + } + if (finish <= start) { + throw new Error(`END marker must appear after BEGIN marker in ${filePath}`); + } + + const before = doc.slice(0, start + begin.length); + const after = doc.slice(finish); + + return `${before}\n\n${block}\n\n${after}`; +} + +await main(); 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