Lumoswitch Docs
Agent setup

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

MethodAvailable?Implementation
One-time launchYesCRUSH_GLOBAL_CONFIG points to a temporary crushrc
Persistent useYesAgent-supported crushrc path + optional launcher
RemovalYesDelete only dedicated files, never the Crush data directory

Prepare the connection values

PlaceholderValue
{{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'
crush

For everyday use, run:

crush

The 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-clean

This 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-compat for Lumoswitch, not openai, which targets native OpenAI behavior.
  • crushrc is 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 --follow for 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 block
  • futureCommand: crush
  • Display order: 120
  • Last verified: 2026-08-27

On this page