From skillry-runtime-and-local-app
Use when you need to prefer a local service plus browser UI over desktop runtime complexity unless a native shell is explicitly required.
How this skill is triggered — by the user, by Claude, or both
Slash command
/skillry-runtime-and-local-app:07-browser-first-local-appThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Default a local tool to a **local HTTP service plus a browser UI** instead of a packaged desktop shell, unless a native shell is genuinely required. Browser-first removes packaging and code-signing overhead, makes the app inspectable through browser devtools from day one, and keeps debugging in familiar territory (network tab, console, breakpoints). The deliverable is a service with a health en...
Default a local tool to a local HTTP service plus a browser UI instead of a packaged desktop shell, unless a native shell is genuinely required. Browser-first removes packaging and code-signing overhead, makes the app inspectable through browser devtools from day one, and keeps debugging in familiar territory (network tab, console, breakpoints). The deliverable is a service with a health endpoint, a launcher that gates the browser-open on readiness, file-based logs, and a clean shutdown that frees the port.
GET /health returning 200 with a tiny JSON body) that reflects real readiness — DB connected, migrations applied — not merely "process is up."localhost (127.0.0.1) by default. Only bind 0.0.0.0 when another device must reach it, and call that out as an exposure decision./health until ready (with a timeout), opens the default browser cross-platform, tees logs to a file, and traps signals for clean shutdown.GET /health endpoint exists and returns 200 only when the app is truly ready.localhost by default; any 0.0.0.0 bind is justified.lsof -i :PORT returning empty.open / xdg-open / start "").*.# Confirm the target port is free before launch
lsof -i :3000 || echo "port 3000 free"
# Cross-platform health-gated launcher
#!/usr/bin/env bash
set -euo pipefail
PORT="${PORT:-3000}"; LOG="${LOG:-./run.log}"
npm run start >"$LOG" 2>&1 & SRV=$!
trap 'kill "$SRV" 2>/dev/null' EXIT INT TERM
for i in $(seq 1 60); do
curl -fsS "http://localhost:$PORT/health" >/dev/null 2>&1 && break
sleep 0.5
[ "$i" = 60 ] && { echo "server not ready, see $LOG"; exit 1; }
done
case "$(uname -s)" in
Darwin) open "http://localhost:$PORT" ;;
Linux) xdg-open "http://localhost:$PORT" ;;
*) start "" "http://localhost:$PORT" ;;
esac
wait "$SRV"
# Verify readiness and shutdown manually
curl -fsS -w '\n%{http_code}\n' http://localhost:3000/health
# after Ctrl-C, confirm no orphan holds the port:
lsof -i :3000 || echo "port released cleanly"
// Health endpoint that reflects REAL readiness (Express)
app.get('/health', async (_req, res) => {
try {
await db.query('SELECT 1'); // dependency check, not just "process up"
res.status(200).json({ status: 'ok' });
} catch (e) {
res.status(503).json({ status: 'degraded' });
}
});
# FastAPI equivalent
@app.get("/health")
async def health():
try:
await database.execute("SELECT 1")
return {"status": "ok"}
except Exception:
return JSONResponse({"status": "degraded"}, status_code=503)
# Auto-select a free port when a fixed one may be taken (Node one-liner)
PORT=$(node -e 'const s=require("net").createServer();s.listen(0,()=>{console.log(s.address().port);s.close()})')
echo "using port $PORT"
# Confirm the server is bound to localhost, not 0.0.0.0 (exposure check)
lsof -nP -iTCP:"$PORT" -sTCP:LISTEN | rg -q '127.0.0.1|\[::1\]' && echo "localhost only" || echo "WARNING: bound on all interfaces"
# Full cold-start verification in one pass (ready -> open -> shutdown -> port free)
./run.sh & LP=$!
until curl -fsS http://localhost:3000/health >/dev/null 2>&1; do sleep 0.5; done
echo "ready"; curl -fsS -w 'home=%{http_code}\n' -o /dev/null http://localhost:3000/
kill "$LP"; sleep 1
lsof -i :3000 && echo "FAIL: orphan holds the port" || echo "PASS: port released"
# CORS check: the local API should reject a foreign origin
curl -s -o /dev/null -w 'cors=%{http_code}\n' -H "Origin: http://evil.example" \
-X OPTIONS http://localhost:3000/api/data # expect a restricted/!200 preflight
# No secrets leaked into the run log
rg -nEi "api[_-]?key|secret|token|password" run.log && echo "WARNING: secret in log" || echo "log clean"
| Requirement | Browser-first covers it? |
|---|---|
| Local files via picker / drag-drop | yes (File System Access API or upload) |
| Local network / localhost service | yes |
| Arbitrary filesystem path access | no — native shell |
| System tray / global hotkeys | no — native shell |
| Signed, auto-updating offline binary | no — native shell |
| Native OS menus / multi-window | no — native shell |
| Inspectable with devtools out of the box | yes (advantage of browser-first) |
sleep 2 races startup; on a slow machine the browser loads a connection-refused page. Always poll /health./health returns 200 the instant the process starts, before the DB pool or migrations are ready, so the first real request fails. Make health reflect dependencies.0.0.0.0 by default. Exposes the dev server to the local network (and anything on it) without intent. Bind localhost unless remote access is a stated requirement.?token=... puts it in browser history and server logs. Use headers or a session cookie instead.until curl loop hangs forever if the server crashes on boot. Cap the poll and fail closed with a log pointer.* on the local API. A dev convenience that lets any site in the browser call the local service. Restrict the origin to the app's own URL.http://localhost:3000 in the client. Breaks the moment the port auto-selects. Inject the base URL at startup.Return: the shell decision (browser-first vs native) with rationale; the server, port, and health endpoint; the launcher script and its log location; and the cold-start verification result (ready → browser opened → clean shutdown, port freed). Note any native-shell requirement that overrode the browser-first default.
localhost by default; never expose the dev server on 0.0.0.0 without a stated reason.Done means the app runs as a localhost service with a health-gated launcher, the browser opens only after readiness, logs land at the policy path with no secrets, and shutdown is clean with no leaked port or process — with any native-shell exception documented.
npx claudepluginhub fluxonlab/skillry --plugin skillry-runtime-and-local-appCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.