Documentation

smug and Glazier are close relatives: one Go binary each, a project file that can live next to the code and a YAML or HCL file that a human can read in one sitting. smug’s file is shorter than a .glaze file, and it has a stop list, which Glazier does not. If it works for you, keep using it. I am not here to convince anyone. But if you want to try a .glaze file, and you already have a directory of smug projects, this page translates one into the other block by block, and it is honest about the places where the two tools simply do not line up.

A typical project

A smug project with the usual parts:

~/.config/smug/daemon-run.yml YAML
session: daemon-run
root: ~/runs/arasaka

env:
  ICE_TARGET: arasaka-mainframe

before_start:
  - docker compose up -d

stop:
  - docker compose stop

attach_hook: echo jacked-in

windows:
  - name: editor
    root: ice
    layout: main-vertical
    selected: true
    commands:
      - nvim .
    panes:
      - type: horizontal
        commands:
          - npm run dev
  - name: logs
    layout: even-horizontal
    manual: true
    commands:
      - tail -f logs/dev.log
    panes:
      - type: horizontal
        commands:
          - htop

The same workspace as a profile:

.glaze HCL
session {
  name               = "daemon-run"
  starting_directory = "~/runs/arasaka"
  commands           = ["docker compose up -d"]

  envs = {
    ICE_TARGET = "arasaka-mainframe"
  }

  hooks = {
    "client-attached" = "run-shell 'echo jacked-in'"
  }

  window {
    name               = "editor"
    layout             = "main-vertical"
    starting_directory = "ice"
    focus              = true

    pane {
      commands = ["nvim ."]
    }

    pane {
      commands = ["npm run dev"]
    }
  }

  window {
    name   = "logs"
    layout = "even-horizontal"

    pane {
      commands = ["tail -f logs/dev.log"]
    }

    pane {
      commands = ["htop"]
    }
  }
}

The logs window lost its manual: true, and the stop list is gone. Both are explained below.

The translation table

smugglazeNotes
sessionsession.nameGlazier replaces ., :, $ and a backslash with - and shows a warning.
rootsession.starting_directory~ expands. A relative path is relative to the directory of the profile.
windows[].namewindow.name
windows[].rootwindow.starting_directoryIn smug, relative to the session root. In Glazier, relative to the directory of the profile.
layoutwindow.layoutThe same five tmux presets. A raw layout string also works in both tools.
selectedfocus = true on the window
windows[].commandsthe first pane.commandssmug runs them in the first pane of the window. See below.
panes[].typenoneThe window layout places the panes. See below.
panes[].rootpane.starting_directoryRelative to the directory of the profile.
panes[].commandspane.commandsA list, in order. Glazier waits for each command except the last.
envsession.envsGlazier applies them before it creates any window or pane.
before_startsession.commands, or run it before glaze upNot the same. See below.
stopnoneSee below.
attach_hook, detach_hooksession.hooksA tmux client-attached or client-detached hook. Not the same. See below.
manualnoneSee below.
smug start daemon-runglaze up
smug start daemon-run:editornoneGlazier creates every window of the profile.
smug stop daemon-runglaze downA session that is not running causes no error.

The five layout presets are tmux presets in both tools: even-horizontal, even-vertical, main-horizontal, main-vertical and tiled. The default in Glazier is tiled.

What does not map one to one

Window commands and pane type. In smug a window starts as one pane, and that pane runs the window’s commands. Each entry in panes then splits a new pane off it, horizontal or vertical, and runs its own commands. Glazier has no implicit first pane and no split direction. Every pane is a pane block, in file order, and the window layout places them. So move the window commands into a first pane block, add one pane block for each entry in panes and drop type. When no preset matches the shape that your splits produced, build the window once by hand and run glaze save: it captures the exact geometry as a raw layout string that up replays. See Window and Save a session.

before_start. smug runs these commands in a shell before the session exists. Glazier has commands on the session block, and they run in the active pane after Glazier creates all windows and panes. The command runs, but later, and in a pane that you can see. That is what the example above does with docker compose up -d. For a command that must finish before anything else, run it before glaze up in your shell. See Session.

stop. smug runs these commands before it kills the session. Glazier has no such hook. glaze down kills the session and does nothing else. Put the commands in front of it in your shell, docker compose stop && glaze down, or in the Makefile of the project. See glaze down and Scripting.

manual. smug skips a manual window unless you ask for it with -w or project:window. Glazier creates every window in the profile, every time. There is no flag to pick windows. When you need the split, give the manual windows a second profile with their own session name and start it with --profile-path. Otherwise drop the key, as the example does, and live with one more window.

attach_hook and detach_hook. These are shell commands. A Glazier hooks entry is a tmux command bound to a tmux hook, so wrap the shell command in run-shell, as the example does with client-attached. tmux fires client-attached on every attach and client-detached on every detach, not only for the first or the last client. Glazier checks the hook name against the names that tmux 3.2a to 3.7c know. See Hooks and options.

Differences in the workflow

  • smug keeps projects in ~/.config/smug/, or in .smug.yml in the current directory. Glazier looks for .glaze in the current directory, then in $GLAZE_PATH, or where --profile-path points. A profile lives with the project, so it goes into the repository. See Profile resolution.
  • Glazier has typed variable blocks and --var flags, so one profile serves many projects. See One profile for many projects.
  • smug types each command with send-keys. Glazier loads the commands into a tmux paste buffer and waits for each pane command except the last with tmux wait-for, so npm install finishes before npm run dev starts. See Run commands reliably.

Check the translation

Run glaze format --validate before the first glaze up. It decodes the profile and reports each problem that it can find, with the file and the line, before tmux starts:

Console
$ glaze format --validate --stdout > /dev/null
Error: Invalid layout specified

  on /home/v/runs/arasaka/.glaze line 9, in session:
   9:     layout = "main-diagonal"

The layout value of "main-diagonal" is not a supported preset
(even-horizontal, even-vertical, main-horizontal, main-vertical, tiled) nor a
valid tmux layout string.

the glaze profile contains errors

Without --stdout, format also rewrites the profile into the canonical form, which is a fine first step for a file you just typed by hand. See glaze format.

Two files, same workspace. Keep the one you prefer.