lcplocal-control-panel

lcp · terminal panel for macOS · Go · v0.1

Every project on one screen.

lcp is a keyboard-driven terminal panel for the folder where your projects live. It shows each project's stack, git state, running servers, disk use and age. One keystroke opens a project, starts its dev server, pulls it, cleans its build folders or archives it. Every action goes into a hash-chained audit log you can read inside the panel.

This page takes keys too: 1–5 jump to a section, j k scroll, g goes back to the top.

Rendered by lcp -snapshot against a demo folder. Every project, account and remote in it is made up.
Demo

See it in eighteen seconds.

A real lcp session on the demo folder: filters, a running server, a cleanup, fetch and pull, and the audit log. The screens are captured from the app; the key labels and highlights are added on top.

1 Screens

Read a project's state from one row.

lcp finds every git repo up to three levels under your projects folder, groups folders that only hold repos, and keeps watching for new clones. Git and ports refresh every ten seconds while it's open. Sizes are measured in the background and cached.

PROJECT ▾ demo-client/ 9 repos acme-ui @acme-corp

The path under your root. Folders that only hold repos fold into groups, and archive/ starts folded.

STACK Tauri Next.js Spring Go Node+Docker

Read from files: src-tauri/, next.config.*, pom.xml, Cargo.toml, go.mod, Compose files and package.json dependencies.

GIT main ✚2 ?2 ↑1 ↓3 no git

Branch, modified, untracked, ahead and behind. Folders with no git show up too, so work without history doesn't go unnoticed.

RUNNING ● :8050 ▶ :4100 ◆2

Listening ports and Docker containers, matched to the project by working directory. ▶ takes the place of ● when the panel started the server, so it's one you can stop from here.

DISK · BUILD 90M 90M 140M 140M

Total size, and how much of it is node_modules, target, .next, dist and other build folders. Nested repos aren't counted twice.

AGE 2d 51d 8mo 2y

Days since the last commit, or the newest file change in a folder without git. The colour is its activity band.

active ≤ 14d warm ≤ 90d dormant ≤ 365d abandoned

Eight filters, one key each

1All
Everything under the root, as a tree.
2Dirty
Repos with modified or untracked files.
3Unpushed
Commits that exist only on this Mac.
4No git
Folders with no history and no backup.
5Running
Anything listening on a port or running in Docker.
6Reclaim
Dormant or abandoned projects still holding build folders.
7Not mine
Remotes owned by someone other than your github_users.
8No deps
Projects with dependencies but no node_modules.

S cycles the sort: tree, recent, size, build size, changes. / searches by path.

Four more screens

  • enterProject detail. Uncommitted changes, recent commits, the README's first lines, package.json scripts and the last ten audit entries for that project.
  • LRuns. Every dev server the panel started, its PID, pane and state, with a live tail of its log refreshed every second.
  • aAudit. The whole log, newest first, with search, a failures-only toggle and a check of the hash chain.
  • ?Help. Every key and the legend, in two columns on terminals 140 columns or wider.
2 Keys

One keystroke from the selected row.

Almost everything you do with a project ends in a terminal: an editor, a shell, Claude Code, a dev server. lcp asks herdr to open those in the project's own workspace, so getting there takes one key.

Get around

Works with or without herdr.

jkMove down and up
gGTop and bottom; ctrl+d ctrl+u by half a page
enterProject detail, or fold a group
/Fuzzy search by path
1–8Filters
RRescan everything and remeasure sizes
FShow the folder in Finder
a?Audit log; help

Work on a project

Talks to the herdr session the panel runs in.

oSwitch to the project's workspace, creating it if needed
eYour editor in a new pane there
cClaude Code in a new pane there
rRun the dev server; the first time, you confirm or edit the command
sStop the run the panel started
LRuns screen with the live log

Keep things tidy

Anything that moves files asks first.

fgit fetch --all in every repo with a remote, six at a time
pgit pull --ff-only for the selected repo
igit init a folder that has no git
xPreview build folders, then move them to the Trash
AArchive to archive/, or restore from it
DMove the whole project to the Trash; you type its path to confirm
3 Safety

It moves things to the Trash. It never deletes them.

lcp sits next to work you care about, so the rules below are fixed in code rather than left to settings. It changes nothing in your projects beyond the actions listed here.

What x shows on metrics-console in the demo. Nothing moves until you press y, and saying no is logged too.

  • Stops only what it started

    A dev server is stopped by its recorded PID, after checking the process still has the same start time. lcp never kills by name or pattern.

  • Cleans from an allowlist

    Only node_modules, target, .next, dist, build, .turbo and src-tauri/target, directly in the project, and never while it's running.

  • Shows the plan, then asks

    Clean, archive, restore, init and fetch all preview what will happen and wait for y. Trashing a whole project needs its path typed out.

  • Moves, never deletes

    Clean and trash use the macOS Trash, and archive is a rename. Finder's Put Back undoes them until you empty the Trash.

  • Git that can't lose work

    Fetch, pull --ff-only, init, status and log. Never push, reset, checkout, stash or force. A repo that wants a password fails instead of prompting.

  • No shell strings

    Commands run as argument lists, so paths with spaces are safe. The one shell, the dev-server wrapper, is built with strict quoting.

  • Stays inside your folder

    Every path an action touches must resolve, after symlinks, inside the projects folder or the archive.

  • One action at a time

    While an action runs, the next one is refused with wait. Git status runs without taking the index lock, so your editor never fights it.

4 Audit

Every action leaves a trail you can check.

The log is plain JSON Lines in ~/.local/state/local-control-panel/audit/, one file per month, append-only and synced after every line. It is never pruned, and the panel never rewrites it.

  • Two lines per action

    An intent line before it runs and a result line after, sharing one ULID. If the panel crashes halfway, the intent is still there.

  • The exact command

    Each entry records the project, its remote, the argv, the key that triggered it, the outcome, exit code and duration. A clean lists every path it moved and its size.

  • A hash chain

    Each line's prev is the SHA-256 of the line before it. Edit or remove a line and the Audit screen shows exactly where the chain breaks.

  • Secrets masked

    GitHub and API tokens, bearer headers, password=-style values and credentials in URLs become ****. Environment variable values are never logged.

auditchain ok · 5 lines 2026-11.jsonl · sample

A sample log with made-up projects. The check is the same rule the Audit screen uses: hash each line's exact bytes with SHA-256 and compare the result with the next line's prev. It runs in your browser.

5 Install

Build it, point it at your folder, press a key.

lcp is a single Go binary. It needs no server, opens no network port and keeps its own files outside your projects.

  1. Check the requirements

    • macOS 15 or later. Cleanup uses the system's /usr/bin/trash.
    • Go 1.24 or later.
    • herdr 0.8.0 or later for the actions that open panes and run servers. Without it the dashboard still works, the top bar says herdr off, and actions tell you why they can't run.
  2. Install

    $ go install github.com/10mTech/local-control-panel/cmd/lcp@latest

    Run it again to update. It installs lcp into $(go env GOPATH)/bin; make sure that is on your PATH, or set GOBIN to install somewhere that already is.

  3. Point it at your projects

    The first run writes ~/.config/local-control-panel/config.toml with defaults. Every key is optional; set root to your projects folder and github_users to your accounts.

    root = "~/Projects"
    scan_depth = 3                       # how deep to look for git repos
    archive = "~/Projects/archive"
    editor = "nvim"
    refresh_seconds = 10                 # git and port refresh while open
    github_users = ["your-github-name"]  # other owners show as "not mine"
    
    [activity]                           # days since the last change
    active = 14
    warm = 90
    dormant = 365                        # older is abandoned
    
    # Per-project overrides, keyed by path under root
    [projects."news-reader"]
    dev = ["pnpm", "tauri", "dev"]
    port = 1420
  4. Run it in a herdr pane

    Type lcp. A few flags help outside the terminal UI:

    -list
    Print the scan as plain text and exit.
    -snapshot dashboard -size 160x45
    Render one screen to stdout. Also takes help, audit, runs and detail:<path>. The images on this page came from it.
    -config PATH
    Use another config file, with its own audit log and runs when it sets state_dir.
    -version
    Print the version.
  5. Uninstall

    Stop the dev servers the panel started (s on the Runs screen), quit, and move the binary to the Trash. Removing the config and state folders is optional; keep the state folder if you want the audit history.

    $ trash "$(go env GOPATH)/bin/lcp"
    $ trash ~/.config/local-control-panel ~/.local/state/local-control-panel

    If you set XDG_CONFIG_HOME, XDG_STATE_HOME or state_dir, remove those folders instead. Nothing lives inside your projects, and anything lcp cleaned, trashed or archived stays where it is.

Troubleshooting

lcp: command not found
Go's bin folder isn't on your PATH. Add export PATH="$HOME/go/bin:$PATH" to ~/.zshrc and open a new terminal.
herdr off
lcp can't reach herdr. Install herdr 0.8.0 or later and start lcp from a herdr pane. The dashboard works without it.
fetch or pull fails
lcp never prompts for a password. Set up an SSH key or a credential helper, then try again. The error is in the Audit screen (a).
unknown keys ignored
A key in config.toml is misspelled or not supported. The warning names it.
anything else
Check debug.log in ~/.local/state/local-control-panel, then open an issue.

Small on purpose.

Local only

No server, no network listener, no telemetry. The only network traffic is the git fetch and pull you ask for.

Not a window manager

herdr owns workspaces and panes. lcp asks it to act and reads its JSON replies.

Mac, single user

It relies on macOS's Trash, lsof and Finder, and it watches one projects folder.

Its files stay apart

Audit log, run logs and caches live in ~/.local/state/local-control-panel, so scans and cleanups never touch them.