Configure T3 Code
T3 Code reads configuration from three places. Two are hand-editable; one is owned by the app.
| What | Where | Edit by hand? |
|---|---|---|
| Project scripts, project icon | t3.json at the repo root |
Yes — commit it |
| Keyboard shortcuts | ~/.t3/userdata/keybindings.json |
Yes |
| App settings, providers, models | ~/.t3/userdata/settings.json |
No — use the UI |
A dev-mode server (vp run dev) uses ~/.t3/dev/ instead of ~/.t3/userdata/.
The published docs say keybindings live at
~/.t3/keybindings.json. That is wrong — the code joins the filename to the state directory, so the real path is~/.t3/userdata/keybindings.json. Editing the documented path does nothing.
After any edit, check your work:
python3 validate.py # ./t3.json and your keybindings.json
python3 validate.py path/to/t3.json
This matters more than usual here: T3 Code silently ignores invalid rules and invalid files. A typo means your shortcut quietly never fires, with only a warning in the server log.
Project scripts — the "actions" in the scripts menu
Scripts are the buttons in T3 Code's scripts menu. They run in a T3 Code terminal at the project root, and they are checked into the repo so the whole team gets them.
{
"$schema": "https://t3.codes/schema/t3.json",
"iconPath": "assets/logo.svg",
"scripts": [
{
"name": "Dev server",
"command": "pnpm dev",
"icon": "play",
"previewUrl": "http://localhost:3000",
"autoOpenPreview": true
},
{
"name": "Setup Worktree",
"command": "pnpm install && ln -sf $T3CODE_PROJECT_ROOT/.env .env",
"icon": "configure",
"runOnWorktreeCreate": true
}
]
}
| Field | Required | Notes |
|---|---|---|
name |
yes | Shown in the menu |
command |
yes | Shell command, run at the project root |
icon |
no | One of play, test, lint, configure, build, debug. Defaults to play |
runOnWorktreeCreate |
no | Runs automatically after a worktree is created |
previewUrl |
no | Opens in the preview panel. Desktop app only |
autoOpenPreview |
no | Opens the preview the moment the script starts. Needs previewUrl |
Limits: 50 scripts, iconPath at most 512 characters.
Two environment variables
Scripts get these injected:
T3CODE_PROJECT_ROOT— the project's main directory, always setT3CODE_WORKTREE_PATH— the worktree path, only when running in a worktree
This distinction is the point. A script's working directory is the worktree
when one exists, so T3CODE_PROJECT_ROOT is how you reach back to the main
checkout — which is how you symlink a gitignored .env into a fresh worktree.
Gotchas that cost real time
- Only the first
runOnWorktreeCreatescript runs. Marking a second one has no effect and no warning. Combine them into one setup script with&&. previewUrlis desktop-only. In a browser tab it is ignored and you open the URL yourself.autoOpenPreviewwithoutpreviewUrldoes nothing.- Scripts are shared with everyone who opens the repo. Don't put machine-specific
paths or secrets in a
command.
Keyboard shortcuts
~/.t3/userdata/keybindings.json is a JSON array. Your file replaces the
defaults wholesale, so copy the defaults first if you only want to change one
thing.
[
{ "key": "mod+j", "command": "terminal.toggle" },
{ "key": "mod+shift+d", "command": "terminal.splitVertical", "when": "terminalFocus" },
{ "key": "mod+1", "command": "script.browser.run" }
]
key and command are required; when is optional. Maximum 256 rules.
Modifiers: mod (⌘ on macOS, Ctrl elsewhere), cmd/meta, ctrl/control,
shift, alt/option.
Commands:
sidebar.toggle rightPanel.toggle commandPalette.toggle
terminal.toggle terminal.split terminal.splitVertical
terminal.new terminal.close diff.toggle
preview.toggle preview.refresh preview.focusUrl
preview.zoomIn preview.zoomOut preview.resetZoom
chat.new chat.newLocal editor.openFavorite
modelPicker.toggle modelPicker.jump.1-9
thread.previous thread.next thread.jump.1-9
script.{id}.run
when contexts: terminalFocus, terminalOpen, previewFocus,
previewOpen, modelPickerOpen. Operators: !, &&, ||, parentheses.
Unknown context names evaluate to false, so a typo disables the binding
rather than erroring.
Precedence: rules are evaluated in array order, and for a given key press the last matching rule wins — across all commands, not just within one. If a shortcut isn't working, look for a later rule that shadows it.
Binding a key to your own script
script.{id}.run needs the script's id, which is not in t3.json — T3 Code
assigns it when the script is imported, as a lowercase slug of the name
("Browser" → browser). Read the real ids from the state database:
sqlite3 ~/.t3/userdata/state.sqlite \
"SELECT title, scripts_json FROM projection_projects WHERE scripts_json != '[]';"
The id must match ^[a-z0-9][a-z0-9-]*$ and be at most 24 characters. validate.py
cross-checks the ids you reference against the ones that actually exist.
App settings
~/.t3/userdata/settings.json holds app behavior and provider configuration:
enableAssistantStreaming,enableProviderUpdateChecksautomaticGitFetchInterval,newWorktreesStartFromOrigindefaultThreadEnvMode—localor worktree, for new threadsaddProjectBaseDirectory— where the "add project" picker startstextGenerationModelSelection— the model used for commit messages and titlesproviders/providerInstances— per-driver config forcodex,claudeAgent,cursor,grok,opencode, including binary paths and custom modelsobservability
Change these in the UI, not the file. The server owns this file and rewrites it; a hand edit made while T3 Code is running will be overwritten without warning. Read it to answer questions, and to confirm a settings change landed.
Note this file configures which providers T3 Code can drive. It does not
configure the agents themselves — Codex still reads ~/.codex/config.toml, and
MCP servers are configured per-provider, not in T3 Code.
Answering "why isn't my config working?"
Work down this list:
- Run
validate.py— it catches most of it. - Check the path.
~/.t3/keybindings.json(documented) is not the real one. - For a shortcut, look for a later rule with the same key — last match wins.
- For a
whenclause, confirm every context name is in the list above. - For a script, restart or reopen the project so
t3.jsonis re-read. - Check the server log for parse warnings:
~/.t3/userdata/logs/server.log.