Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ gh pr view <number> --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.
Expand Down Expand Up @@ -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
6 changes: 3 additions & 3 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
14 changes: 11 additions & 3 deletions docs/agentic-git-identity.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`)

<Info>This is optional but recommended. You can also use your main account with a different email/name.</Info>
<Info>
This is optional but recommended. You can also use your main account with a different email/name.
</Info>

## Step 2: Generate Classic GitHub Token

Expand Down Expand Up @@ -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.

<Info>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.)</Info>
<Info>
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.)
</Info>

## Step 4: Configure GitHub Authentication

Expand Down Expand Up @@ -101,4 +106,7 @@ git config --global credential.helper ""
git config --global --add credential.helper '!gh auth git-credential'
```

<Warning>The "replace all" approach will disable platform keychain helpers and may break Git authentication for non-GitHub remotes (GitLab, Bitbucket, etc.).</Warning>
<Warning>
The "replace all" approach will disable platform keychain helpers and may break Git authentication
for non-GitHub remotes (GitLab, Bitbucket, etc.).
</Warning>
4 changes: 3 additions & 1 deletion docs/context-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,9 @@ Remove oldest 50% of messages.

### OpenAI Responses API Limitation

<Warning>`/truncate` does not work with OpenAI models due to the Responses API architecture:</Warning>
<Warning>
`/truncate` does not work with OpenAI models due to the Responses API architecture:
</Warning>

- OpenAI's Responses API stores conversation state server-side
- Manual message deletion via `/truncate` doesn't affect the server-side state
Expand Down
15 changes: 12 additions & 3 deletions docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

<Info>Windows builds are only available from [releases](https://github.com/coder/mux/releases), not from development builds.</Info>
<Info>
Windows builds are only available from [releases](https://github.com/coder/mux/releases), not from
development builds.
</Info>

To download:

Expand Down Expand Up @@ -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

<Warning>Windows support is currently in alpha. Please [report any issues](https://github.com/coder/mux/issues) you encounter.</Warning>
<Warning>
Windows support is currently in alpha. Please [report any
issues](https://github.com/coder/mux/issues) you encounter.
</Warning>

### Testing Pre-Release Builds

<Warning>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:</Warning>
<Warning>
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:
</Warning>

1. After installing, open Terminal
2. Run: `xattr -cr /Applications/Mux.app`
Expand Down
5 changes: 4 additions & 1 deletion docs/keybinds.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<Info>This document should be kept in sync with `src/utils/ui/keybinds.ts`, which is the source of truth for keybind definitions.</Info>
<Info>
This document should be kept in sync with `src/utils/ui/keybinds.ts`, which is the source of truth
for keybind definitions.
</Info>

## Platform Conventions

Expand Down
22 changes: 22 additions & 0 deletions docs/models.md → docs/models.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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 <provider:model_id>`.

{/* 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)
Expand Down
12 changes: 9 additions & 3 deletions docs/runtime/local.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,15 @@ Local runtime runs the agent directly in your project directory—the same direc

## Caveats

<Warning>**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.</Warning>

<Warning>**Affects your working copy**: Agent changes happen in your actual project directory.</Warning>
<Warning>
**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.
</Warning>

<Warning>
**Affects your working copy**: Agent changes happen in your actual project directory.
</Warning>

## Filesystem

Expand Down
4 changes: 3 additions & 1 deletion docs/runtime/ssh.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ Host ovh-1

## Authentication

<Info>As we delegate to `ssh`, this is really an abbreviated reference of how `ssh` authenticates.</Info>
<Info>
As we delegate to `ssh`, this is really an abbreviated reference of how `ssh` authenticates.
</Info>

There are a few practical ways to set up authentication.

Expand Down
1 change: 0 additions & 1 deletion docs/system-prompt.md → docs/system-prompt.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions fmt.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
129 changes: 129 additions & 0 deletions scripts/gen_docs.ts
Original file line number Diff line number Diff line change
@@ -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> | string;
}

async function syncDoc(config: SyncConfig): Promise<SyncResult> {
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<string> {
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();
Loading