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.
local-control-panel 37 repos · 7 no git · 15 dirty · 2 unpushed · 1 running scanned 20:21:59 in 214ms 1 All 2 Dirty 15 3 Unpushed 2 4 No git 7 5 Running 1 6 Reclaim 8 7 Not mine 22 8 No deps 17 sort: tree ╭─ Projects ───────────────────────────────────────────────────────────────────────────────────────╮╭─ metrics-console ──────────────────────────────╮ │ PROJECT STACK GIT RUNNING DISK BUILD AGE ││ path metrics-console │ │ metrics-console @de… Vite main 20M 20M 15mo ││ kind git repo │ │ ▾ sandbox/ 2 repos ││ stack Vite │ │ landing-demo no d… Next.js main local 180K 51d ││ │ │ ▾ portal-site/ 1 repo ││ branch main │ │ frontend no deps Next.js main 188K 2mo ││ upstream origin/main ↑0 ↓0 │ │ ▾ katas-project/ 1 repo ││ changes 0 modified, 0 untracked │ │ katas-project no … Next.js main ✚2 ?2 12M 12M 8mo ││ commit 15mo ago · commit dist for static ho…│ │ notify-bot Node+Docker main ● :4100 22M 22M 2d ││ remote git@github.com:demo-dev-archive/metr…│ │ tool-starter main ?1 160K 20mo ││ owner demo-dev-archive · not yours │ │ ▾ demo-client/ 9 repos ││ │ │ acme-document @ac… Spring master 192K 17mo ││ running nothing │ │ acme-storage @acm… Spring master ✚2 192K 17mo ││ dev port 5173 from Vite default │ │ acme-ui @acme-corp Vite master ✚1 75M 75M 16mo ││ │ │ acme-user @acme-c… Spring master ✚1 192K 16mo ││ disk 20M measured 5m ago │ │ ▾ archived-services/ 5 repos ││ node_modules 20M │ │ acme-api-gatewa… Spring dev ?2 200K 2y ││ dist 4K │ │ acme-docs @lega… Spring dev ✚1 180K 2y ││ activity abandoned · last change 2025-08-07 │ │ acme-tenant @le… Spring master ✚1 ?4 196K 2y ││ │ │ acme-tenant-cod… Spring dev 180K 2y ││ │ │ acme-ui @legacy… Vite master 45M 45M 2y ││ │ │ docs-template no de… Next.js master 176K 2y ││ │ │ widget-playground n… Next.js main 176K 10mo ││ │ │ news-reader Tauri main 140M 140M 1d ││ │ │ contact-forms no de… Vite master 188K 21mo ││ │ │ reference-notes no … Next.js main ?1 192K 6mo ││ │ │ ▾ practice/ 1 repo ││ │ │ practice-notes no… Next.js main ?1 204K 4d ││ │ │ uml-diagrams @demo-… master 168K 23mo ││ │ │ local-control-panel Go main 200K 1d ││ │ │ image-resizer-ui-v2… Tauri no git 40M 40M 8mo ││ │ ╰─────────────────────────────────────────────────────────────────────────────────────────── 1/52 ─╯╰─────────────────────────────── enter for more ─╯ ? help enter detail o open F finder e edit c claude r run s stop L runs f fetch p pull x clean A archive D trash / search 1-8 filter …
local-control-panel 37 repos · 7 no git · 15 dirty · 2 unpushed · 1 running scanned 20:22:05 in 221ms ╭─ notify-bot ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ path notify-bot │ │ kind git repo │ │ stack Node+Docker │ │ │ │ branch main │ │ upstream origin/main ↑0 ↓0 │ │ changes 0 modified, 0 untracked │ │ commit 2d ago · fix rounding in totals │ │ remote git@github.com:demo-dev/notify-bot.git │ │ owner demo-dev · yours │ │ │ │ running :4100 node pid 19762 external │ │ dev port 4100 from .env │ │ │ │ disk 22M measured 5m ago │ │ node_modules 22M │ │ activity active · last change 2026-10-30 │ │ │ │ Uncommitted changes │ │ clean │ │ │ │ Recent commits │ │ f6e317c 2 days ago fix rounding in totals │ │ 6b45843 3 days ago docker setup │ │ 8ae695b 4 days ago weekly summary job │ │ 5f4d3b9 5 days ago category rules │ │ acc72c0 6 days ago parse alert rules │ │ 9910238 7 days ago init bot skeleton │ │ │ │ README │ │ # notify-bot │ │ Demo bot that reads build alerts and posts a short weekly digest to your team chat. │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 0% ─╯ j/k scroll o open F finder e edit c claude r run s stop p pull x clean A archive D trash L runs R reload esc back
lcp -snapshot against a demo folder. Every project, account and remote in it is made up.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.
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.
▾ 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.
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.
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.
● :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.
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.
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.
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.
| jk | Move down and up |
| gG | Top and bottom; ctrl+d ctrl+u by half a page |
| enter | Project detail, or fold a group |
| / | Fuzzy search by path |
| 1–8 | Filters |
| R | Rescan everything and remeasure sizes |
| F | Show the folder in Finder |
| a? | Audit log; help |
Work on a project
Talks to the herdr session the panel runs in.
| o | Switch to the project's workspace, creating it if needed |
| e | Your editor in a new pane there |
| c | Claude Code in a new pane there |
| r | Run the dev server; the first time, you confirm or edit the command |
| s | Stop the run the panel started |
| L | Runs screen with the live log |
Keep things tidy
Anything that moves files asks first.
| f | git fetch --all in every repo with a remote, six at a time |
| p | git pull --ff-only for the selected repo |
| i | git init a folder that has no git |
| x | Preview build folders, then move them to the Trash |
| A | Archive to archive/, or restore from it |
| D | Move the whole project to the Trash; you type its path to confirm |
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.
Move these folders to the Trash: node_modules 20M Left alone: target symlink, never followed dist holds files tracked in git Total 20M. Reinstall or rebuild to get them back. The space is freed when you empty the Trash.y move to Trash · n cancel
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,.turboandsrc-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.
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
previs 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.
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.
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.
-
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.
- macOS 15 or later. Cleanup uses the system's
-
Install
$ go install github.com/10mTech/local-control-panel/cmd/lcp@latestRun it again to update. It installs
lcpinto$(go env GOPATH)/bin; make sure that is on yourPATH, or setGOBINto install somewhere that already is. -
Point it at your projects
The first run writes
~/.config/local-control-panel/config.tomlwith defaults. Every key is optional; setrootto your projects folder andgithub_usersto 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
-
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,runsanddetail:<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.
-
Uninstall
Stop the dev servers the panel started (
son 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_HOMEorstate_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. Addexport PATH="$HOME/go/bin:$PATH"to~/.zshrcand open a new terminal. - herdr off
- lcp can't reach herdr. Install herdr 0.8.0 or later and start
lcpfrom 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.tomlis misspelled or not supported. The warning names it. - anything else
- Check
debug.login~/.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.