Crush Agent setup
Crush is Charm's terminal coding agent for inspecting and editing projects, running commands, switching models, and using MCP from a TUI. It is a CLI product. Its “desktop notifications” are system alerts when the terminal is unfocused, not a standalone Desktop app, and there is no official IDE extension.
Create a Lumoswitch API configuration with OpenAI-compatible output first. Crush now recommends crushrc; the older crush.json remains compatible but is no longer preferred.
Choose a setup method
| Method | Available? | Implementation |
|---|---|---|
| One-time launch | Yes | CRUSH_GLOBAL_CONFIG points to a temporary crushrc |
| Persistent use | Yes | Agent-supported crushrc path + optional launcher |
| Removal | Yes | Delete only dedicated files, never the Crush data directory |
Prepare the connection values
| Placeholder | Value |
|---|---|
{{api_base_url}} | The downstream API URL, including /v1 |
{{access_key}} | The Lumoswitch Access Key selected for this configuration |
{{model}} | The client-facing model name shown by the API configuration |
{{crush_config_text}} | Generated Crush configuration containing every downstream model and verified reasoning flags |
The examples use a 128000 context window and 8192 maximum output as placeholders. Replace them with the real model capabilities; Crush uses these values for context and output management.
One-time launch
(
set -e
LUMOSWITCH_CRUSH_CONFIG="$(mktemp "${TMPDIR:-/tmp}/lumoswitch-crush.XXXXXX")"
trap 'rm -f "$LUMOSWITCH_CRUSH_CONFIG"' EXIT
cat > "$LUMOSWITCH_CRUSH_CONFIG" <<'LUMOSWITCH_CRUSH_CONFIG_EOF'
{{crush_config_text}}LUMOSWITCH_CRUSH_CONFIG_EOF
chmod 600 "$LUMOSWITCH_CRUSH_CONFIG"
env \
LUMOSWITCH_CRUSH_API_KEY="{{access_key}}" \
CRUSH_GLOBAL_CONFIG="$LUMOSWITCH_CRUSH_CONFIG" \
crush --model "lumoswitch/{{model}}"
)The temporary file and Key exist only in the subprocess and are removed on exit. A project .crushrc or crushrc has higher precedence than global configuration. Check project configuration if the selected model is replaced.
Persistent use
set -e
LUMOSWITCH_ROOT="$HOME/.config/lumoswitch/agents/crush"
LUMOSWITCH_ENV="$LUMOSWITCH_ROOT/env.sh"
LUMOSWITCH_LAUNCHER="$HOME/.local/bin/lumoswitch-crush"
mkdir -p "$LUMOSWITCH_ROOT" "$(dirname "$LUMOSWITCH_LAUNCHER")"
umask 077
if [ -f "$LUMOSWITCH_LAUNCHER" ]; then
LUMOSWITCH_BACKUP="$LUMOSWITCH_LAUNCHER.backup.$(date +%Y%m%d-%H%M%S)-$$"
cp "$LUMOSWITCH_LAUNCHER" "$LUMOSWITCH_BACKUP"
chmod 600 "$LUMOSWITCH_BACKUP"
fi
LUMOSWITCH_CRUSH_CONFIG="$LUMOSWITCH_ROOT/crushrc"
cat > "$LUMOSWITCH_CRUSH_CONFIG" <<'LUMOSWITCH_CONFIG_EOF'
{{crush_config_text}}LUMOSWITCH_CONFIG_EOF
cat > "$LUMOSWITCH_ENV" <<'LUMOSWITCH_ENV_EOF'
export CRUSH_GLOBAL_CONFIG="$HOME/.config/lumoswitch/agents/crush/crushrc"
export LUMOSWITCH_CRUSH_API_KEY="{{access_key}}"
export LUMOSWITCH_CRUSH_API_BASE_URL="{{api_base_url}}"
export LUMOSWITCH_CRUSH_MODEL="{{model}}"
LUMOSWITCH_ENV_EOF
case "$SHELL" in
*/zsh) LUMOSWITCH_PROFILE="$HOME/.zshrc" ;;
*/bash) LUMOSWITCH_PROFILE="$HOME/.bashrc" ;;
*) LUMOSWITCH_PROFILE="$HOME/.profile" ;;
esac
touch "$LUMOSWITCH_PROFILE"
LUMOSWITCH_PROFILE_MARKER='# Lumoswitch Agent: crush'
LUMOSWITCH_SOURCE_LINE='[ -f "$HOME/.config/lumoswitch/agents/crush/env.sh" ] && . "$HOME/.config/lumoswitch/agents/crush/env.sh"'
if ! grep -Fqx "$LUMOSWITCH_SOURCE_LINE" "$LUMOSWITCH_PROFILE"; then
printf '\n%s\n%s\n' "$LUMOSWITCH_PROFILE_MARKER" "$LUMOSWITCH_SOURCE_LINE" >> "$LUMOSWITCH_PROFILE"
fi
printf '%s\n' "$LUMOSWITCH_PROFILE" > "$LUMOSWITCH_ROOT/profile-path"
cat > "$LUMOSWITCH_LAUNCHER" <<'LUMOSWITCH_LAUNCHER_EOF'
#!/usr/bin/env bash
set -e
LUMOSWITCH_ROOT="$HOME/.config/lumoswitch/agents/crush"
LUMOSWITCH_ENV="$LUMOSWITCH_ROOT/env.sh"
LUMOSWITCH_LAUNCHER="$HOME/.local/bin/lumoswitch-crush"
if [ "$1" = "--lumoswitch-clean" ]; then
if [ -f "$LUMOSWITCH_ROOT/profile-path" ]; then
LUMOSWITCH_PROFILE="$(cat "$LUMOSWITCH_ROOT/profile-path")"
if [ -f "$LUMOSWITCH_PROFILE" ]; then
LUMOSWITCH_PROFILE_MARKER='# Lumoswitch Agent: crush'
LUMOSWITCH_SOURCE_LINE='[ -f "$HOME/.config/lumoswitch/agents/crush/env.sh" ] && . "$HOME/.config/lumoswitch/agents/crush/env.sh"'
LUMOSWITCH_PROFILE_TMP="$LUMOSWITCH_PROFILE.lumoswitch.$$"
awk -v marker="$LUMOSWITCH_PROFILE_MARKER" -v source="$LUMOSWITCH_SOURCE_LINE" '$0 != marker && $0 != source' "$LUMOSWITCH_PROFILE" > "$LUMOSWITCH_PROFILE_TMP"
mv "$LUMOSWITCH_PROFILE_TMP" "$LUMOSWITCH_PROFILE"
fi
fi
if [ -d "$LUMOSWITCH_ROOT" ]; then
find "$LUMOSWITCH_ROOT" -depth \( -type f -o -type l \) -delete
find "$LUMOSWITCH_ROOT" -depth -type d -exec rmdir {} \; 2>/dev/null || true
fi
find "$(dirname "$LUMOSWITCH_LAUNCHER")" -maxdepth 1 -type f -name 'lumoswitch-crush.backup.*' -delete
rm -f "$LUMOSWITCH_LAUNCHER"
printf '%s\n' 'Lumoswitch native setup and optional launcher removed. Open a new terminal to refresh the environment.'
exit 0
fi
if [ -f "$LUMOSWITCH_ENV" ]; then . "$LUMOSWITCH_ENV"; fi
exec crush "$@"
LUMOSWITCH_LAUNCHER_EOF
chmod 600 "$LUMOSWITCH_ENV"
find "$LUMOSWITCH_ROOT" -type f -exec chmod 600 {} \;
chmod 700 "$LUMOSWITCH_LAUNCHER"
. "$LUMOSWITCH_ENV"
printf '%s\n' 'Native Lumoswitch setup saved. Future command: crush'
printf '%s\n' 'Optional maintenance launcher: ~/.local/bin/lumoswitch-crush'
crushFor everyday use, run:
crushThe crushrc references an Agent-specific environment variable. Its Key is stored in the owner-only managed environment file, not in the optional launcher.
Native Windows PowerShell import
These commands run natively in Windows PowerShell without WSL and use the same connection values as the macOS/Linux templates on this page.
One-time use on Windows
& {
$ErrorActionPreference = 'Stop'
$lumoswitchConfig = Join-Path ([IO.Path]::GetTempPath()) ('lumoswitch-' + [Guid]::NewGuid().ToString('N') + '.json')
$lumoswitchConfigContent = @'
{{crush_config_text}}
'@
[IO.File]::WriteAllText($lumoswitchConfig, $lumoswitchConfigContent, [Text.UTF8Encoding]::new($false))
$lumoswitchEnvironmentNames = @('CRUSH_GLOBAL_CONFIG', 'LUMOSWITCH_CRUSH_API_KEY')
$lumoswitchPreviousEnvironment = @{}
foreach ($lumoswitchName in $lumoswitchEnvironmentNames) {
$lumoswitchPreviousEnvironment[$lumoswitchName] = [Environment]::GetEnvironmentVariable($lumoswitchName, 'Process')
}
$lumoswitchExitCode = 0
try {
[Environment]::SetEnvironmentVariable('CRUSH_GLOBAL_CONFIG', $lumoswitchConfig, 'Process')
[Environment]::SetEnvironmentVariable('LUMOSWITCH_CRUSH_API_KEY', '{{access_key}}', 'Process')
$lumoswitchArguments = @('--model', 'lumoswitch/{{model}}')
$lumoswitchExecutable = Get-Command 'crush.cmd' -CommandType Application -ErrorAction SilentlyContinue
if ($null -eq $lumoswitchExecutable) { $lumoswitchExecutable = Get-Command 'crush' -CommandType Application -ErrorAction Stop }
& $lumoswitchExecutable.Path @lumoswitchArguments
if ($null -ne $LASTEXITCODE) { $lumoswitchExitCode = $LASTEXITCODE }
} finally {
foreach ($lumoswitchName in $lumoswitchEnvironmentNames) {
[Environment]::SetEnvironmentVariable($lumoswitchName, $lumoswitchPreviousEnvironment[$lumoswitchName], 'Process')
}
Remove-Item -LiteralPath $lumoswitchConfig -Force -ErrorAction SilentlyContinue
}
if ($lumoswitchExitCode -ne 0) { throw 'crush exited with code ' + $lumoswitchExitCode }
}Persistent use on Windows
This command backs up existing Lumoswitch-owned files and restricts access to the configuration and launcher for the current user.
& {
$ErrorActionPreference = 'Stop'
$lumoswitchRoot = Join-Path $env:LOCALAPPDATA 'Lumoswitch\agents\crush'
$lumoswitchLauncher = Join-Path $env:LOCALAPPDATA 'Lumoswitch\bin\lumoswitch-crush.ps1'
$lumoswitchBackupSuffix = (Get-Date -Format 'yyyyMMdd-HHmmss') + '-' + $PID
function Protect-LumoswitchFile([string] $Path) {
$lumoswitchSid = [Security.Principal.WindowsIdentity]::GetCurrent().User.Value
& icacls.exe $Path '/inheritance:r' '/grant:r' ('*' + $lumoswitchSid + ':(F)') '*S-1-5-18:(F)' '*S-1-5-32-544:(F)' | Out-Null
if ($LASTEXITCODE -ne 0) { throw "Could not restrict access to $Path" }
}
function Install-LumoswitchFile([string] $Path, [string] $Content) {
[IO.Directory]::CreateDirectory((Split-Path -Parent $Path)) | Out-Null
if (Test-Path -LiteralPath $Path) {
$lumoswitchItem = Get-Item -LiteralPath $Path -Force
if ($lumoswitchItem.PSIsContainer -or ($lumoswitchItem.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
throw "Lumoswitch refused to replace a directory or link: $Path"
}
$lumoswitchBackup = $Path + '.backup.' + $lumoswitchBackupSuffix
Copy-Item -LiteralPath $Path -Destination $lumoswitchBackup
Protect-LumoswitchFile $lumoswitchBackup
}
$lumoswitchTemporary = $Path + '.' + [Guid]::NewGuid().ToString('N') + '.tmp'
try {
[IO.File]::WriteAllText($lumoswitchTemporary, $Content, [Text.UTF8Encoding]::new($false))
Move-Item -LiteralPath $lumoswitchTemporary -Destination $Path -Force
Protect-LumoswitchFile $Path
} finally {
Remove-Item -LiteralPath $lumoswitchTemporary -Force -ErrorAction SilentlyContinue
}
}
[IO.Directory]::CreateDirectory($lumoswitchRoot) | Out-Null
$lumoswitchCrushConfig = Join-Path $lumoswitchRoot 'crushrc'
$lumoswitchConfigContent = @'
{{crush_config_text}}
'@
Install-LumoswitchFile $lumoswitchCrushConfig $lumoswitchConfigContent
$lumoswitchEnvironment = [ordered]@{}
$lumoswitchEnvironment['CRUSH_GLOBAL_CONFIG'] = (Join-Path $lumoswitchRoot 'crushrc')
$lumoswitchEnvironment['LUMOSWITCH_CRUSH_API_KEY'] = '{{access_key}}'
$lumoswitchEnvironment['LUMOSWITCH_CRUSH_API_BASE_URL'] = '{{api_base_url}}'
$lumoswitchEnvironment['LUMOSWITCH_CRUSH_MODEL'] = '{{model}}'
foreach ($lumoswitchEntry in $lumoswitchEnvironment.GetEnumerator()) {
[Environment]::SetEnvironmentVariable($lumoswitchEntry.Key, $lumoswitchEntry.Value, 'User')
[Environment]::SetEnvironmentVariable($lumoswitchEntry.Key, $lumoswitchEntry.Value, 'Process')
}
$lumoswitchLauncherContent = @'
param([Parameter(ValueFromRemainingArguments = $true)][string[]] $AgentArgs)
$ErrorActionPreference = 'Stop'
$lumoswitchRoot = Join-Path $env:LOCALAPPDATA 'Lumoswitch\agents\crush'
$lumoswitchLauncher = Join-Path $env:LOCALAPPDATA 'Lumoswitch\bin\lumoswitch-crush.ps1'
function Remove-LumoswitchFile([string] $Path) {
if (-not (Test-Path -LiteralPath $Path)) { return }
$lumoswitchItem = Get-Item -LiteralPath $Path -Force
if ($lumoswitchItem.PSIsContainer -or ($lumoswitchItem.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
throw "Lumoswitch refused to remove a directory or link: $Path"
}
Remove-Item -LiteralPath $Path -Force
}
if ($AgentArgs.Count -gt 0 -and $AgentArgs[0] -eq '--lumoswitch-clean') {
foreach ($lumoswitchName in @('CRUSH_GLOBAL_CONFIG', 'LUMOSWITCH_CRUSH_API_KEY', 'LUMOSWITCH_CRUSH_API_BASE_URL', 'LUMOSWITCH_CRUSH_MODEL')) {
[Environment]::SetEnvironmentVariable($lumoswitchName, $null, 'User')
[Environment]::SetEnvironmentVariable($lumoswitchName, $null, 'Process')
}
if (Test-Path -LiteralPath $lumoswitchRoot -PathType Container) {
$lumoswitchRootItem = Get-Item -LiteralPath $lumoswitchRoot -Force
if ($lumoswitchRootItem.Attributes -band [IO.FileAttributes]::ReparsePoint) { throw 'Lumoswitch refused to clean through a linked directory.' }
foreach ($lumoswitchFile in @(Get-ChildItem -LiteralPath $lumoswitchRoot -File -Recurse -Force)) { Remove-LumoswitchFile $lumoswitchFile.FullName }
foreach ($lumoswitchDirectory in @(Get-ChildItem -LiteralPath $lumoswitchRoot -Directory -Recurse -Force | Sort-Object FullName -Descending)) {
if ($null -eq (Get-ChildItem -LiteralPath $lumoswitchDirectory.FullName -Force | Select-Object -First 1)) { Remove-Item -LiteralPath $lumoswitchDirectory.FullName -Force }
}
if ($null -eq (Get-ChildItem -LiteralPath $lumoswitchRoot -Force | Select-Object -First 1)) { Remove-Item -LiteralPath $lumoswitchRoot -Force }
}
$lumoswitchBin = Split-Path -Parent $lumoswitchLauncher
if (Test-Path -LiteralPath $lumoswitchBin -PathType Container) {
foreach ($lumoswitchBackup in @(Get-ChildItem -LiteralPath $lumoswitchBin -Filter 'lumoswitch-crush.ps1.backup.*' -File -Force)) { Remove-LumoswitchFile $lumoswitchBackup.FullName }
}
Remove-LumoswitchFile $lumoswitchLauncher
Write-Host 'Lumoswitch native setup and optional launcher removed. Open a new terminal to refresh the environment.'
exit 0
}
$lumoswitchEnvironment = [ordered]@{}
$lumoswitchEnvironment['CRUSH_GLOBAL_CONFIG'] = (Join-Path $lumoswitchRoot 'crushrc')
$lumoswitchEnvironment['LUMOSWITCH_CRUSH_API_KEY'] = '{{access_key}}'
$lumoswitchEnvironment['LUMOSWITCH_CRUSH_API_BASE_URL'] = '{{api_base_url}}'
$lumoswitchEnvironment['LUMOSWITCH_CRUSH_MODEL'] = '{{model}}'
foreach ($lumoswitchEntry in $lumoswitchEnvironment.GetEnumerator()) {
[Environment]::SetEnvironmentVariable($lumoswitchEntry.Key, $lumoswitchEntry.Value, 'Process')
}
$lumoswitchExecutable = Get-Command 'crush.cmd' -CommandType Application -ErrorAction SilentlyContinue
if ($null -eq $lumoswitchExecutable) { $lumoswitchExecutable = Get-Command 'crush.exe' -CommandType Application -ErrorAction SilentlyContinue }
if ($null -eq $lumoswitchExecutable) { $lumoswitchExecutable = Get-Command 'crush' -CommandType Application -ErrorAction Stop }
$lumoswitchArguments = @()
& $lumoswitchExecutable.Path @lumoswitchArguments @AgentArgs
if ($null -ne $LASTEXITCODE) { exit $LASTEXITCODE }
'@
Install-LumoswitchFile $lumoswitchLauncher $lumoswitchLauncherContent
Write-Host 'Native Lumoswitch setup saved. Future command: crush'
Write-Host 'Optional maintenance launcher:' $lumoswitchLauncher
$lumoswitchExecutable = Get-Command 'crush.cmd' -CommandType Application -ErrorAction SilentlyContinue
if ($null -eq $lumoswitchExecutable) { $lumoswitchExecutable = Get-Command 'crush.exe' -CommandType Application -ErrorAction SilentlyContinue }
if ($null -eq $lumoswitchExecutable) { $lumoswitchExecutable = Get-Command 'crush' -CommandType Application -ErrorAction Stop }
$lumoswitchArguments = @()
& $lumoswitchExecutable.Path @lumoswitchArguments
if ($null -ne $LASTEXITCODE -and $LASTEXITCODE -ne 0) { throw 'crush exited with code ' + $LASTEXITCODE }
}The import writes the Agent-supported persistent configuration and starts crush directly. The saved Lumoswitch maintenance script is not required to start the Agent; run it with --lumoswitch-clean when you need to restore or remove this setup.
Remove the Lumoswitch setup
Windows PowerShell
Exit every Lumoswitch session for this Agent, then run the command below. The optional maintenance launcher removes the managed native configuration, restores any preserved shared file, clears Lumoswitch user environment values, and removes itself.
& {
$lumoswitchLauncher = Join-Path $env:LOCALAPPDATA 'Lumoswitch\bin\lumoswitch-crush.ps1'
if (-not (Test-Path -LiteralPath $lumoswitchLauncher -PathType Leaf)) {
throw 'The optional Lumoswitch maintenance launcher is missing. Run persistent import again before cleanup.'
}
& powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File $lumoswitchLauncher --lumoswitch-clean
}macOS / Linux
"$HOME/.local/bin/lumoswitch-crush" --lumoswitch-cleanThis does not remove ~/.config/crush/crushrc, project crushrc, session data, or other providers. If you manually merged provider add lumoswitch into shared configuration, remove only that provider and its model command rather than deleting the whole file.
Desktop and IDE boundaries
Crush officially ships only as a terminal agent. It can send system desktop notifications, but there is no separate Desktop app or IDE panel that needs another Lumoswitch configuration. Running these commands in an IDE's integrated terminal still starts the same Crush CLI process, with the same one-time and persistent behavior.
Verify and troubleshoot
- Use
openai-compatfor Lumoswitch, notopenai, which targets native OpenAI behavior. crushrcis trusted code. Never run Crush in a repository whose configuration you have not reviewed.- Test a normal response, then tools and long context. Use
crush logs --followfor diagnostics.
References: the official Crush repository and configuration, Custom Providers, and the configuration schema. Last verified: 2026-08-27.
Maintainer publication fields
- Platform ID:
crush - Display name:
Crush CLI - Downstream protocol:
openai-compatible commandTemplate: copy the complete One-time launch code block- Persistent strategy:
native persistentCommandTemplate: copy the complete Persistent use code blockfutureCommand:crush- Display order:
120 - Last verified:
2026-08-27