Reference

Configuration

Everything you can put in a project config file.

Project

A project is one app you want to manage with lpm — a website, an API, a blog, anything you’d normally start in a terminal. Every config begins with two things: a name and the folder it lives in.

project.ymlvalid

myapp

No active terminals

Click Start to run myapp, or open a terminal.

New Terminal
name: myapp
root: ~/Projects/myapp
services:
  web: npm run dev

You don’t have to write this from scratch

The easiest way to add a project is from the desktop app: click the + button in the sidebar, point it at your project folder, and lpm will create the file for you and detect your services. You can always come back and edit it later from the app.

FieldTypeDescription
name*stringThe label you’ll see in the sidebar of the desktop app. Pick something short you’ll recognize at a glance, like myapp or blog.
root*stringThe folder on your computer where this project lives. Every other path in the config (like cwd) is interpreted relative to this folder. ~ is a shortcut for your home directory (e.g. ~/Projects/myapp).

* required

Services

Services are the long-running processes that make up your project — your dev server, an API, a background worker, anything you’d normally leave running in a terminal tab. lpm starts them together when you open the project and stops them when you’re done. Every project needs at least one service.

If a command runs continuously, it’s a service. If it finishes and exits — tests, a build, a migration — it belongs in Actions instead. Each service can be written as a one-line shorthand (just the command) or as the full form when you need cwd, port, or env.

services.ymlvalid

myapp

No active terminals

Click Start to run myapp, or open a terminal.

New Terminal
name: myapp
root: ~/Projects/myapp
services:
  # shorthand — key is the name, value is the command
  web: npm run dev

  # full form — use this when you need cwd, port, or env
  server:
    cmd: node server.js
    cwd: ./server             # run from a subfolder (great for monorepos)
    port: 4000                # unique per project; shown as a link in the app
    env:                      # extra env vars just for this service
      API_KEY: dev-secret
FieldTypeDescription
cmd*stringThe shell command that starts the process — exactly what you’d type into a terminal yourself, like npm run dev or node server.js. lpm keeps it running and shows its output in the app.
cwdstringStart the service from a different folder than the project root — handy for monorepos where each app lives in its own subfolder like ./apps/web. Relative paths resolve from root, and ~ expands to your home directory.
portintThe port this service listens on. lpm uses it to show a clickable link in the toolbar and to warn you if something else is already bound. Each port must be unique across services in the same project.
envmapExtra environment variables to set just for this service — things like API_URL or NODE_ENV. Useful when you don’t want to commit them to a .env file.
profiles[]stringNames of the profiles this service belongs to. Profiles let you start a named subset of services instead of everything at once — see the Profiles section below.

* required

Starting and stopping

You don’t start services one by one. Click the project in the sidebar and lpm spins them all up together; click stop in the toolbar and they all come down. If you only want a subset running — say, the web app without the worker — define a profile and pick it from the project switcher.

Actions

Actions are the commands you run once in a while — your test suite, a database migration, a deploy script. Services run continuously; actions fire once, do their job, and show you the result. Click one in the desktop app toolbar (or tuck it into the three-dot menu) and lpm runs it for you.

Try it — click test or Deploy to Production in the preview above. Actions with confirm: true ask before running; everything else just runs.

actions.ymlvalid

myapp

No active terminals

Click Start to run myapp, or open a terminal.

New Terminal
name: myapp
root: ~/Projects/myapp
services:
  web: npm run dev
actions:
  test: npm test              # shorthand

  deploy:                     # full form
    cmd: ./scripts/deploy.sh
    label: Deploy to Production  # display name in the UI
    confirm: true             # ask before running
    display: header           # main button row (default; omit to get the same)
    env:
      NODE_ENV: production
FieldTypeDescription
cmdstringThe shell command to run — whatever you’d type into a terminal yourself. Required unless the action groups child actions with actions below.
labelstringThe friendly name shown on the button. If you skip it, lpm uses the action’s key — e.g. test becomes test.
cwdstringRun the command from a different folder than the project root — useful for monorepos or when the action lives in a subfolder. Relative paths resolve from root. ~ expands to your home directory. Nested actions inherit this from their parent.
envmapExtra environment variables to set just for this action — handy for one-off flags like NODE_ENV=production. Nested actions inherit these from their parent.
confirmboolShow a confirmation dialog before running. Turn this on for anything you’d regret clicking by accident — deletes, resets, production deploys.
displaystringWhere the action appears. The default, header, pins it to the project toolbar so it’s always one click away — omit display entirely to get the same result. footer tucks it into the status bar below the terminal — handy for tight, always-visible controls. menu still works as a legacy value (overflow menu) but is no longer suggested by the editor.
typestringHow the action runs. The default pops open a modal and streams output while the command runs. terminal opens a persistent interactive pane (see the terminals: section). backgroundruns the command hidden and shows a toast when it finishes — perfect for slow, boring commands whose only interesting signal is “did it succeed.”
actionsmapGroup related commands under this action. They show up as a dropdown. If the parent also has a cmd, it renders as a split button — clicking the main part runs the parent, clicking the chevron opens the group.

Shorthand. If all your action needs is a command, write it as a single line — the key becomes the label and you skip the nested form entirely. Great for everyday dev commands:

actions-shorthand.ymlvalid

myapp

No active terminals

Click Start to run myapp, or open a terminal.

New Terminal
name: myapp
root: ~/Projects/myapp
services:
  web: npm run dev
actions:
  test: npm test
  lint: npm run lint
  build: npm run build
  typecheck: npx tsc --noEmit
  format: npx prettier --write .

Destructive actions. For anything you don’t want to click by accident — cache wipes, rollbacks, production deploys — add confirm: true to get a confirmation dialog, and pair it with display: header(the default) so the action lives in the toolbar where you’ll find it:

actions-destructive.ymlvalid

myapp

No active terminals

Click Start to run myapp, or open a terminal.

New Terminal
name: myapp
root: ~/Projects/myapp
services:
  web: npm run dev
actions:
  reset-cache:
    cmd: rm -rf .next node_modules/.cache
    label: Reset Cache
    confirm: true
    display: header
  rollback:
    cmd: ./scripts/rollback.sh
    label: Rollback Deploy
    confirm: true
    env:
      NODE_ENV: production

Grouping related actions. Give a parent action both a cmd and nested actionsand lpm renders it as a split button: the main part runs the parent’s command, the chevron opens a menu with the children. Use this when there’s a sensible default plus a few alternatives — like “Deploy staging” with production and preview tucked behind it:

actions-nested.ymlvalid

myapp

No active terminals

Click Start to run myapp, or open a terminal.

New Terminal
name: myapp
root: ~/Projects/myapp
services:
  web: npm run dev
actions:
  deploy:
    cmd: ./deploy.sh staging     # split button — main click runs this
    label: 🚀 Deploy
    display: header
    confirm: true
    actions:                     # chevron opens these
      production:
        cmd: ./deploy.sh production
        label: 🔴 Production
        confirm: true
      preview:
        cmd: ./deploy.sh preview
        label: 👁️ Preview

Dropdown-only groups. Drop the parent’s cmd and the whole button becomes a dropdown. Good for a set of related commands with no obvious default — like a database toolkit (Migrate, Seed, Reset):

actions-dropdown.ymlvalid

myapp

No active terminals

Click Start to run myapp, or open a terminal.

New Terminal
name: myapp
root: ~/Projects/myapp
services:
  web: npm run dev
actions:
  db:
    label: 🗄️ Database
    display: header
    cwd: ./backend
    actions:
      migrate:
        cmd: python manage.py migrate
        label: 📦 Migrate
      seed:
        cmd: python manage.py seed
        label: 🌱 Seed
      reset:
        cmd: python manage.py flush
        label: 💣 Reset
        confirm: true

Background actions. For slow, boring commands where the only thing you care about is whether they succeeded — builds, migrations, docker pulls, git fetch — add type: background. The command runs hidden in the background and lpm pops a toast when it’s done, success or failure. No modal to dismiss, no terminal tab to clean up, and you can fire several in parallel while you keep working:

actions-background.ymlvalid

myapp

No active terminals

Click Start to run myapp, or open a terminal.

New Terminal
name: myapp
root: ~/Projects/myapp
services:
  web: npm run dev
actions:
  db-reset:
    cmd: npm run db:reset && npm run db:seed
    label: Reset DB
    type: background       # runs hidden, notifies on completion
    confirm: true          # pair with confirm for destructive ones

A note on inheritance

Nested actions inherit cwd and env from their parent unless they override them. Set cwd: ./backend on the parent once and every child runs from there — no need to repeat yourself.

Terminals

Terminals are persistent interactive shells you can open from the desktop app with a single click — a live log tail, a Node or Python REPL, or an AI coding agent like Claude Code waiting in the sidebar. Unlike a service, a terminal isn’t something lpm starts and stops for you; unlike an action, it doesn’t run once and exit. It stays open, you type in it, and it remembers where you left off until you close it.

Under the hood, the terminals section is shorthand for actions with type: terminal. Same fields, same features — entries just default to type: terminal so they open in a pane and stay open. Use terminals: to keep them grouped, or drop a type: terminal entry inside actions: when you want to mix one-shots and persistent shells together.

Shorthand vs. full form. If the command is all you need, a single line is enough — the key becomes the label. Reach for the full form when you want a friendlier label, tuck a terminal into the footer with display: footer, or set a cwd or env:

terminals.ymlvalid

myapp

No active terminals

Click Start to run myapp, or open a terminal.

New Terminal
name: myapp
root: ~/Projects/myapp
services:
  web: npm run dev
terminals:
  codex: codex                # shorthand — key becomes the label

  claude:                     # full form
    cmd: claude
    label: Claude Code        # nicer name than the key
    display: header           # pin to the toolbar, one click away (default)
FieldTypeDescription
cmd*stringThe shell command that starts the terminal — usually something interactive you want to keep around, like claude, node, or tail -f ./logs/dev.log. lpm opens it in a real PTY so prompts, colors, and arrow keys all work.
labelstringThe friendly name shown on the button or in the menu. If you skip it, lpm uses the terminal’s key — so claude just shows up as claude.
cwdstringOpen the terminal in a different folder than the project root — useful for monorepos or when your agent should start inside a specific package. Relative paths resolve from root, and ~ expands to your home directory.
envmapExtra environment variables to set just for this terminal — handy for picking a model with ANTHROPIC_MODEL or pointing a REPL at a staging database. These only apply inside this shell, nothing else on your system is touched.
displaystringSame rules as actions. The default, header, pins the terminal to the project toolbar — omit display entirely to get the same result. footer tucks it into the status bar below the terminal. menu still works as a legacy value (overflow menu) but is no longer suggested by the editor.

* required

Header or footer?

Header is the default — every terminal lands in the toolbar unless you say otherwise. Move the tightest, always-one-click controls to the footer with display: footer so they sit beside the branch switcher without crowding the main button row.

AI coding agents, one click away. This is where terminals really shine. List the agents and REPLs you actually use and they’ll be waiting in the sidebar the next time you open the project — no hunting for the right window, no remembering which folder you were in:

terminals-agents.ymlvalid

myapp

No active terminals

Click Start to run myapp, or open a terminal.

New Terminal
name: myapp
root: ~/Projects/myapp
services:
  web: npm run dev
terminals:
  claude: claude              # AI pair programmer
  codex: codex                # another AI agent, swap at will
  node: node                  # quick REPL for poking at things
  logs: tail -f ./logs/dev.log  # live-tail your dev server logs

Profiles

Profiles let you group services into named workflows so you don’t have to spin up everything every time. Working on a CSS tweak? Start just the frontend. Building a new feature end-to-end? Fire up the full stack. Pick the profile you want from the Start button’s dropdown in the project toolbar, and lpm only launches those services.

Start small. Even two profiles pay off right away — a lightweight one for quick UI fixes and a full one for feature work. Here’s the smallest useful setup: a frontend and a backend, with a frontendprofile that skips the API when you don’t need it:

profiles.ymlvalid

myapp

No active terminals

Click Start to run myapp, or open a terminal.

New Terminal
name: myapp
root: ~/Projects/myapp
services:
  web: npm run dev              # frontend UI
  api:
    cmd: node server.js
    port: 4000                  # backend API
profiles:
  # Just the frontend — fastest startup, good for UI-only fixes
  frontend: [web]
  # Full stack — frontend + backend for feature work
  full:     [web, api]

Every name in a profile list must match a service defined above in services. Services can appear in as many profiles as you like — overlap is fine and expected.

Multiple profiles for different modes. Once your project grows a third or fourth service — a background worker, a queue, a second frontend — a single profile isn’t enough. Define one profile per workflow you actually use, so you can jump between “just the UI”, “normal dev”, and “everything running” without touching the config:

profiles-multi.ymlvalid

shop

No active terminals

Click Start to run shop, or open a terminal.

New Terminal
name: shop
root: ~/Projects/shop
services:
  web: npm run dev              # React frontend
  api:
    cmd: python -m api.server
    port: 5000                  # Flask backend
  worker: celery -A tasks       # background jobs
profiles:
  # Quick UI fixes — no backend needed
  frontend: [web]
  # Normal day-to-day development — web + api
  local:    [web, api]
  # Everything, including background workers
  full:     [web, api, worker]

What if I don’t pick a profile?

No problem — profiles are optional. If you hit Start without choosing one from the dropdown, lpm starts every service in your config. Profiles are there for when you want less than everything; skip them entirely if everything is what you want.

Global Config

Most of your config lives per-project, but some things aren’t tied to any one codebase — system maintenance, utilities, your favorite shell. Drop those into ~/.lpm/global.yml and they show up in every project automatically. If a project defines an action or terminal with the same name, the project-level entry wins.

A minimal global file. Two things you’ll reach for in any project: a Docker Prune action to reclaim disk space, and htop as a quick system monitor. Notice there’s no name or root — global config skips both.

~/.lpm/global.ymlvalid

No active terminals

Add a service in the config to get started.

New Terminal
actions:
  docker-prune:
    cmd: docker system prune -f
    label: Docker Prune
    confirm: true             # asks before wiping images and caches

terminals:
  htop: htop                  # live system monitor, one click away

System-wide utilities. A fuller example: prune merged git branches, upgrade Homebrew, and keep a few system monitors one click away. These all live above individual projects — click them from any project and they just work.

~/.lpm/global.ymlvalid

No active terminals

Add a service in the config to get started.

New Terminal
actions:
  prune-branches:
    cmd: git branch --merged main | grep -v main | xargs git branch -d
    label: Prune merged branches
    confirm: true             # deletes local branches — ask first
  brew-upgrade:
    cmd: brew update && brew upgrade
    label: Brew upgrade       # keep Homebrew packages fresh

terminals:
  htop: htop                  # live CPU and memory
  btop: btop                  # prettier process viewer
  ncdu: ncdu ~                # explore what's eating your disk

Only actions and terminals

Global config supports actions and terminals— that’s it. No services, no profiles. Long-running processes and profile groupings always belong to a specific project, so they have to live in a project file.

Recipes

Full working configs you can copy and adapt. The sections above each show one concept in isolation; the recipes here stitch services, actions, and terminals together into configs that mirror how a real project looks on day one. Find the recipe closest to your stack, paste it into a new project, and tweak from there.

Minimal blog. Start here if you just want one dev server and nothing else — a personal blog, a tiny side project, the “hello world” version of lpm. One service, no actions, no ceremony.

blog.ymlvalid

blog

No active terminals

Click Start to run blog, or open a terminal.

New Terminal
name: blog
root: ~/Projects/blog
services:
  web: npm run dev # the only thing you need to hit Start

Blog with tests and linting. Add this when your tests, linter, or build start taking long enough that retyping them feels wasteful. Same dev server as above, plus three one-click buttons in the toolbar.

blog.ymlvalid

blog

No active terminals

Click Start to run blog, or open a terminal.

New Terminal
name: blog
root: ~/Projects/blog
services:
  web: npm run dev
actions:
  # one-click buttons for the chores you used to retype
  test: npm test
  lint: npm run lint
  build: npm run build

Next.js plus a Node API. For the classic two-process web app: a Next.js front-end in the project root and a Node backend in ./server. Shows how to set cwd per service, expose a port, add a guarded deploy, and pin a log tail to its own terminal tab.

webapp.ymlvalid

webapp

No active terminals

Click Start to run webapp, or open a terminal.

New Terminal
name: webapp
root: ~/Projects/webapp
services:
  web: npm run dev # Next.js front-end
  server:
    cmd: node server.js # API the front-end talks to
    cwd: ./server # lives in a subfolder
    port: 4000 # surfaced in the app so you can open it
actions:
  deploy:
    cmd: ./scripts/deploy.sh
    confirm: true # ask before shipping
terminals:
  logs: tail -f ./logs/server.log # keep server logs one click away

Next.js with dev env vars. Pick this when your app needs a handful of environment variables to boot locally and you’re tired of remembering them. lpm injects them every time the service starts — keep real secrets in your own .env file, not here.

webapp.ymlvalid

webapp

No active terminals

Click Start to run webapp, or open a terminal.

New Terminal
name: webapp
root: ~/Projects/webapp
services:
  web:
    cmd: npm run dev
    port: 3000
    env:
      # dev-only values — real secrets belong in your own .env
      API_URL: http://localhost:4000
      NEXTAUTH_SECRET: dev-secret
      NODE_ENV: development

Monorepo with an app and docs. For a repo that holds more than one thing you want running at once — say an app in apps/web and a docs site in apps/docs. Both services live under one project and start together, each from its own folder.

mono.ymlvalid

mono

No active terminals

Click Start to run mono, or open a terminal.

New Terminal
name: mono
root: ~/Projects/mono
services:
  web:
    cmd: npm run dev
    cwd: ./apps/web # one app in the monorepo
    port: 3000
  docs:
    cmd: npm run dev
    cwd: ./apps/docs # another app, started together
    port: 3001

How to use a recipe

Copy the one closest to your stack, change name and root to match your project, then swap in your own commands. If a piece looks unfamiliar, jump back to the matching section above and tinker with its playground — every reference section has one at the top, and your edits stay live until you reload.

Path resolution

  • ~ expands to your home directory
  • Relative cwd paths resolve relative to root
  • Absolute paths are used as-is

Validation

Config is validated on load and save. Validation checks:

  • At least one service is defined
  • All cmd fields are non-empty
  • Ports are in range 0-65535 with no duplicates
  • All cwd paths point to existing directories
  • Profile entries reference existing services