Skip to content
Back to skills

Macos Rebuild Dev

ASecurity

Kill the running termio dev app, rebuild it with SwiftPM, and relaunch it as a foreground app. Invoke when the user says 'rebuild', 'rebuild app', 'restart app', 'relaunch', '重新编译', or '重启 app'.

  • 540 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added September 3, 2026
ai-agentsswiftshellbashgit

Works with

  • cli

Security analysis

A100/100

Scanned September 20, 2026

npx -y skills add termio-sh/termio --skill macos-rebuild-dev --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Macos Rebuild Dev?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Macos Rebuild Dev
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/termio-sh-macos-rebuild-dev/badge)](https://www.skillsdirectory.com/skills/termio-sh-macos-rebuild-dev)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: macos-rebuild-dev
description: "Kill the running termio dev app, rebuild it with SwiftPM, and relaunch it as a foreground app. Invoke when the user says 'rebuild', 'rebuild app', 'restart app', 'relaunch', '重新编译', or '重启 app'."
---

# Rebuild App (local development)

Kill the running **dev** build, rebuild it as the isolated `termio-dev.app` bundle,
and relaunch — so every rebuild reflects the current Dock icon and, crucially, does
**not** disturb an installed release build you use daily.

## The dev channel

termio ships a `TERMIO_CHANNEL=dev` build that is a fully separate app from the
release one, so both can run at the same time:

| | release | dev (this skill) |
| --- | --- | --- |
| Bundle | `termio.app` (usually `/Applications`) | `termio-dev.app` (repo root) |
| Bundle id | `sh.termio.app` | `sh.termio.app.dev` |
| State + sockets | `~/Library/Application Support/termio` | `…/termio-dev` |
| User config | `~/.termio` | `~/.termio-dev` |
| Daemon socket | `$TMPDIR/termiod-<uid>` | `…-dev` |
| Daemon launchd job | `sh.termio.termiod` | `sh.termio.termiod.dev` |
| Companion port | 8787 | 8788 |
| CLI on PATH | `termio` | `termio-dev` |
| Sparkle auto-update | on | **no update feed** (dev never self-updates) |

All of this falls out of the `.dev` bundle-id suffix via `Sources/termio/Companion/AppChannel.swift`
(paths + port) and `scripts/build-app.sh` (id, name, `SUFeedURL` deletion, CLI rebind).
Sparkle.framework itself is embedded on both channels — the binary links it either
way — so a dev bundle still carries it; it just has nothing to check.

termio is a plain SwiftPM executable (`Package.swift` → `executableTarget` named
`termio`); `swift build` alone produces a bare binary with **no Dock icon**. So this
skill builds the real bundle via `scripts/build-app.sh`. For a dev build the script
auto-picks a real codesigning identity from **your own** keychain (any "Apple
Development" / "Developer ID Application" cert — a free Apple ID gives you one) so the
dev app can post macOS notifications; `usernoted` rejects an ad-hoc signature outright,
so an unsigned dev build can **never** banner. A contributor with **no** signing cert
falls back to ad-hoc automatically — the build still succeeds, they just don't get
notifications. Set `SIGN_IDENTITY=…` to force a specific one. The app runs
unsandboxed with `.exec` PTYs — see `CLAUDE.md`. The icon is `packaging/icon-static.svg`,
rasterized to `packaging/AppIcon.png` by `scripts/render-icon.sh` (needs headless
Chrome). Note: dev and release currently share the same icon art and differ only by
name ("termio dev") — a tinted dev icon is a possible follow-up.

## Instructions

When invoked, execute these steps sequentially:

1. **Kill only the running dev app + its tunnel.** Both channels' inner binary is
   named `termio` (`CFBundleExecutable`), so `pkill -x termio` would kill the release
   app too. Match the dev bundle **path** instead. SIGKILL skips the app's
   `willTerminate` cleanup, so also reap the companion tunnel it spawned on the dev
   port (8788):
   ```bash
   pkill -9 -f "termio-dev.app/Contents/MacOS/termio" || true
   pkill -9 -f "cloudflared tunnel --url http://127.0.0.1:8788" || true
   pkill -9 -f "tunelo port 8788" || true
   ```

2. **Build the dev bundle** from the committed `packaging/AppIcon.png` — do *not*
   re-render the icon (see "Icons" below). Show the tail of the output; if the
   build fails, show the error and **stop — do NOT relaunch**:
   ```bash
   TERMIO_CHANNEL=dev ./scripts/build-app.sh 2>&1 | tail -12
   ```

3. **Re-register the dev bundle with LaunchServices, then refresh the Dock cache.**
   macOS resolves an app's icon by its bundle id (`sh.termio.app.dev`); `lsregister -f`
   forces our path to win and `killall Dock` drops the cached icon:
   ```bash
   LSREG="/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister"
   "$LSREG" -f "$PWD/termio-dev.app"
   touch ./termio-dev.app
   killall Dock 2>/dev/null || true
   ```

4. **Relaunch via `open`** (NOT `nohup` on the inner binary — that bypasses
   LaunchServices). `open`'s `--stdout` / `--stderr` still capture logs:
   ```bash
   open ./termio-dev.app --stdout /tmp/termio-dev.log --stderr /tmp/termio-dev.log
   echo "launched ./termio-dev.app (logs: /tmp/termio-dev.log)"
   ```

5. **Upgrade the running dev daemon.** `build-app.sh` already rebuilt the
   daemon into `Resources/termiod`, but a **running** dev daemon keeps its old
   image: a dev app's launch-time reconcile deliberately refuses to stage
   (its bundle stamp is a placeholder, and that guard is what keeps a dev
   build from ever deploying itself over a release daemon). Without this
   step, a termiod-side change gets "verified" against the previous daemon.
   The deploy is idempotent — same version reports `current` and does
   nothing, so it costs nothing on Swift-only rebuilds — and an execve
   handoff keeps the pid and carries every live session. `TERMIOD_SOCK` must
   be unset first: a shell inside a termio session carries it pointed at the
   *release* daemon, and it overrides the channel.
   ```bash
   unset TERMIOD_SOCK
   TERMIO_CHANNEL=dev ./termio-dev.app/Contents/Resources/termiod deploy --json 2>&1 | tail -3
   ```
   A daemon that isn't running needs nothing: the first pane starts it from
   the new bundle.

6. **Report** the result: whether the build succeeded, the app relaunched, and
   the daemon version the deploy reported. If the window doesn't appear, check
   `/tmp/termio-dev.log`.

## Verifying a termiod-side change

Sessions carried across the handoff keep the environment and shell their
**old** daemon spawned them with — a change to session spawning (env,
injection, PTY setup) is invisible in them by design. Open a **new** session
to see it.

## Icons

`scripts/render-icon.sh` rasterizes `packaging/icon-static.svg` into
`packaging/AppIcon.png` (and the iOS asset) through headless Chrome — and its
output is **not reproducible**: rendering the same SVG twice writes two different
files, neither matching the committed one. Running it on every rebuild therefore
dirtied the tree with two unreviewable binary diffs, cost a Chrome launch, and
risked overwriting hand-touched artwork. So it is not part of this loop.

Re-render only when the SVG actually changed, and commit the PNGs as their own
change:

```bash
./scripts/render-icon.sh && git add packaging/AppIcon.png \
  ios/Sources/Assets.xcassets/AppIcon.appiconset/AppIcon.png
```

## Notes

- This builds **release configuration** (via `build-app.sh`) into the dev *channel*
  bundle, so it's a few seconds slower than a bare `swift build`. For quick code-only
  iteration without a bundle, run `swift build` and launch
  `"$(swift build --show-bin-path)/termio"` directly — but that has no Dock icon and,
  running unbundled, uses `AppChannel.suffix == ""` (i.e. the *release* state dir and
  port 8787), so it is **not** isolated. Use the bundle when you need isolation.
- A concurrent SwiftPM process holding the `.build` lock can make a build emit
  spurious errors mid-write; if that happens, just rerun.
- Do NOT modify `Package.swift` or sources during a rebuild.
- After rebuilding a UI change, pair this with the `app-screenshot-debug` skill to
  actually *see* the result.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.