project-app
Skill0 starsChanged 4 months agoMITView on GitHub
Install/control the backlog board's background app; stop with the stop argument. Use with /project-app.
In this repository42 other files it gives its agents.
- .claude/CLAUDE.mdCLAUDE.md
- content-rewriteSkill
- content-writeSkill
- core-auditSkill
- core-bootstrapSkill
- core-commitSkill
- core-explainSkill
- core-finalizeSkill
- core-orchestrateSkill
- core-pullSkill
- core-setupSkill
- core-updateSkill
- design-convertSkill
- design-shipSkill
- design-tokensSkill
- dev-inspectSkill
- dev-learnSkill
- dev-manualSkill
- dev-optimizeSkill
- dev-securitySkill
- dev-shipSkill
- dev-tweakSkill
- game-debugSkill
- game-optimizeSkill
- game-shipSkill
- game-tweakSkill
- marketing-contentSkill
- marketing-researchSkill
- marketing-screenshotsSkill
- project-addSkill
- project-memorySkill
- project-planSkill
- project-removeSkill
- project-researchSkill
- project-seedSkill
- project-syncSkill
- project-todoSkill
- project-tunnelSkill
- team-issuesSkill
- team-outsourceSkill
- team-reviewSkill
- team-verifySkill
The file
We split this skill into the rules it gives an agent
12 rules. About 0 are common, 1 is distinctive, and 11 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
11
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 220
If install-app (PHASE 5) was run, the Scheduled Task only triggers AtLogOn — unlike macOS's KeepAlive, there's no mid-session auto-restart …
- Very specific: line 232
If install-app (PHASE 5) was run, a LaunchAgent restarts the server automatically (KeepAlive) — plain kill alone would just bounce right …
- Very specific: line 246
SERVER_RUNNING = false: start the server. COLD_START=true.
- Distinctive: line 262
Don't block waiting for readiness here — starting it in the background and moving straight on to PHASE 2/3 (both pure filesystem/string …
- Very specific: line 270
Use $root uit PHASE 1 (al resolved).
- Very specific: line 296
If no projects are found, hint toward /project-add or /project-plan.
- Very specific: line 298
If AUTO_INSTALLED=true (from PHASE 0), add one line: "Background service wasn't installed yet — set it up automatically (starts on its own …
- Very specific: line 307
If cwd is directly under the projects root and that subdirectory contains .project/ → use http://localhost:9876/{project-name} (dashboard …
- Very specific: line 348
Always runs (unless argument was stop, which already exited above).
- Very specific: line 361
Use $loading_url in place of $url everywhere below when $COLD_START=true.
- Very specific: line 444
If $COLD_START was true, note that the server was just started and the window shows a loading state until it's reachable.
- Very specific: line 448
Start entry, so minimize/restore works correctly and future /project-app runs auto-detect and use it.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Project App
Day-to-day, you shouldn't need this skill at all — once install-app (PHASE 5) has run
once (automatically, via /core-bootstrap or the first time this skill's default path runs),
the board lives behind a single Dock/Launchpad (macOS) or taskbar/Start (Windows) icon: click it,
the board opens, minimize/restore works natively. This skill is the install/control layer
underneath that icon, plus a fallback opener for before it exists yet:
stop— pause the background service. Closing the PWA window doesn't do this (the LaunchAgent/Scheduled Task keepsserve-backlog.jsrunning on purpose) — this is the only interface to actually stop it.- Root-mismatch self-heal (default path, PHASE 1) — if
projects_rootchanges, the background service keeps serving the old one until something detects and restarts it; the LaunchAgent/Task alone can't do that. install-app— (re)run the full background-service + PWA setup. Needed after a projects-root change, an icon/script update, or on a machine where the auto-install (PHASE 0 /core-bootstrapPHASE 2.5) hasn't run yet or failed (e.g. Node wasn't installed at the time).- Default path as fallback opener — starts the server if needed and opens it in a window.
This still matters for the very first open on a machine: before the PWA is installed there's no
icon to click yet, and not every setup path (e.g.
/project-addrestoring an existing project without going throughcore-setup's greenfield flow) surfaces a link on its own.
Serves all project backlogs and dashboards at http://localhost:9876. The server also serves a
PWA manifest + service worker (/manifest.webmanifest, /sw.js) — the whole origin (index,
dashboards, backlogs, reviews) is installable from the browser's "Install app" prompt for that
permanent icon.
The background process itself installs automatically — serve-backlog.js starts at login
(BACKLOG_IDLE_SHUTDOWN_MS=0 so its idle auto-shutdown never fights the background-restart
mechanism), silently, via /core-bootstrap PHASE 2.5 or this skill's own PHASE 0 the first time
it's found missing (shared/APP-INSTALL-CHECK.md). On macOS this is a LaunchAgent (KeepAlive,
restarts on crash too); on Windows a Scheduled Task (AtLogOn — restarts at next login, not
mid-session, since a detached start has no crash-restart hook). An earlier on-demand-launcher
model needed a second icon for exactly that reason on macOS — a PWA can't start itself —
install-app replaces it and removes the old launcher if found.
Multi-device — same backlog/dashboard content, not real-time: this skill only starts/stops
the local server; it doesn't sync .project/ data by itself. Use /project-sync push (Mac) →
clone the repo + /project-add (or /project-sync pull on an existing clone) on the new
machine to bring the backlog/dashboard content over via the claude/state orphan branch — a
deliberate push/pull step, not continuous sync. Requires a git remote both machines can reach;
only text travels (screenshots/wireframes and credentials don't). See shared/STATE-SYNC.md for
the full mechanics.
Trigger
/project-app — opens the app window (starts the server first if needed; auto-installs the
background service on first run, see PHASE 0). Optional argument: stop, or install-app
(macOS + Windows — force a full background-server + PWA setup/update, see PHASE 5).
Platform
Detect platform:
- Windows:
$PSVersionTablebestaat → PowerShell - macOS: bash
Projects root (first match wins):
- Env var
CLAUDE_PROJECTS_ROOT <config_repo>/.claude/paths.local.yaml→ veldprojects_root(geschreven door/core-bootstrap)- Windows fallback:
C:\Projects - macOS fallback:
$HOME/projects
config_repo = parent van de gederefereerde symlink/junction ~/.claude/skills.
Server-script pad: ~/.claude/skills/shared/references/serve-backlog.js
Process
Execution rule: shell state (variables like $root/$SERVER_RUNNING, function
definitions like resolve_projects_root) does not persist across separate Bash tool
calls — only the working directory does. Run each phase's code blocks (resolve → validate →
branch → start/stop) as one Bash invocation per phase, not split across multiple calls.
Splitting them silently drops $root (empty-string checks then pass/fail unpredictably) and
is the most common cause of this skill misbehaving — it is not a bug in the extraction
commands themselves.
PHASE 0: Ensure background service, then check current status
Sub-step A — auto-install check (default path only). Skip this sub-step entirely when the
argument is stop (don't install just to immediately stop) or install-app (PHASE 5 already
runs the full install unconditionally — this would be redundant). Otherwise:
Todo: Read
.claude/skills/shared/APP-INSTALL-CHECK.mdand follow it.
Store the outcome as AUTO_INSTALLED — true if it returned INSTALLED_NOW, false for
ALREADY_INSTALLED or SKIPPED: <reason> (keep the reason for the PHASE 2 report if skipped).
This is purely additive: whether it installs, skips, or fails, PHASE 1 below still runs its own
existing "start the server if not running" logic regardless — nothing here is a hard dependency.
Sub-step B — status check.
Windows (PowerShell):
try { Invoke-WebRequest -Uri http://localhost:9876/ -UseBasicParsing -TimeoutSec 2 | Out-Null; "RUNNING" } catch { "STOPPED" }macOS (bash):
curl -s http://localhost:9876/ > /dev/null 2>&1 && echo RUNNING || echo STOPPEDStore result as SERVER_RUNNING.
PHASE 1: Execute action
Resolve $root first (altijd, ook als server al draait). If the resolved root does not exist as a directory, prompt for a new path and persist it to paths.local.yaml before continuing:
Windows:
function Resolve-ProjectsRoot {
if ($env:CLAUDE_PROJECTS_ROOT) { return $env:CLAUDE_PROJECTS_ROOT }
$skillsLink = Get-Item "$env:USERPROFILE\.claude\skills" -ErrorAction SilentlyContinue
if ($skillsLink -and $skillsLink.Target) {
$repo = Split-Path -Parent $skillsLink.Target
$yaml = Join-Path $repo ".claude\paths.local.yaml"
if (Test-Path $yaml) {
$line = Select-String -Path $yaml -Pattern '^\s*projects_root:\s*"?([^"\r\n]+)"?' | Select-Object -First 1
if ($line) { return $line.Matches[0].Groups[1].Value.Trim().TrimEnd('"') }
}
}
return "C:\Projects"
}
$root = Resolve-ProjectsRoot
# Validate the resolved root exists. A stale/incorrect projects_root leaves the
# board empty — offer to fix it and persist to paths.local.yaml (the source of truth).
if (-not (Test-Path $root -PathType Container)) {
# AskUserQuestion (open text input): projects_root "$root" does not exist —
# ask for a new absolute path that must exist as a directory. Store in $newRoot.
# (If $env:CLAUDE_PROJECTS_ROOT is set it keeps overriding on the next run — note that.)
$skillsLink = Get-Item "$env:USERPROFILE\.claude\skills" -ErrorAction SilentlyContinue
if ($skillsLink -and $skillsLink.Target) {
$yaml = Join-Path (Split-Path -Parent $skillsLink.Target) ".claude\paths.local.yaml"
if (Test-Path $yaml) {
(Get-Content $yaml -Raw) -replace '(?m)^(\s*projects_root:).*', "`$1 `"$newRoot`"" | Set-Content $yaml
}
}
$root = $newRoot
}
# If a server is already running, verify it serves the resolved root.
$rootMatches = $true
if ($SERVER_RUNNING -eq "RUNNING") {
try {
$served = (Invoke-WebRequest -Uri http://localhost:9876/__root -UseBasicParsing -TimeoutSec 2 | ConvertFrom-Json).root
$servedNorm = try { (Resolve-Path $served -ErrorAction Stop).Path } catch { $served }
$rootNorm = try { (Resolve-Path $root -ErrorAction Stop).Path } catch { $root }
if ($served -and -not ($servedNorm -ieq $rootNorm)) { $rootMatches = $false }
} catch {}
}macOS:
resolve_projects_root() {
if [ -n "$CLAUDE_PROJECTS_ROOT" ]; then printf '%s' "$CLAUDE_PROJECTS_ROOT"; return; fi
local repo
repo="$(cd "$HOME/.claude/skills" 2>/dev/null && pwd -P | sed 's|/[^/]*$||')"
local yaml="$repo/.claude/paths.local.yaml"
if [ -f "$yaml" ]; then
local v
v=$(awk -F'"' '/^[[:space:]]*projects_root:/ {print $2; exit}' "$yaml")
if [ -n "$v" ]; then
v="${v/#\~/$HOME}"; v="${v/#\$HOME/$HOME}"
printf '%s' "$v"; return
fi
fi
printf '%s' "$HOME/projects"
}
root="$(resolve_projects_root)"
# Validate the resolved root exists. A stale/incorrect projects_root leaves the
# board empty — offer to fix it and persist to paths.local.yaml (the source of truth).
if [ ! -d "$root" ]; then
# AskUserQuestion (open text input): projects_root "$root" does not exist —
# ask for a new absolute path that must exist as a directory. Store in NEW_ROOT.
# (If $CLAUDE_PROJECTS_ROOT is set it keeps overriding on the next run — note that.)
repo="$(cd "$HOME/.claude/skills" 2>/dev/null && pwd -P | sed 's|/[^/]*$||')"
yaml="$repo/.claude/paths.local.yaml"
if [ -f "$yaml" ]; then
sed -i '' 's|^\([[:space:]]*projects_root:\).*|\1 "'"$NEW_ROOT"'"|' "$yaml"
fi
root="$NEW_ROOT"
fi
# If a server is already running, verify it serves the resolved root.
ROOT_MATCHES=true
if [ "$SERVER_RUNNING" = RUNNING ]; then
served="$(curl -s http://localhost:9876/__root | sed -n 's/.*"root":"\([^"]*\)".*/\1/p')"
canon() { (cd "$1" 2>/dev/null && pwd -P) || printf '%s' "$1"; }
if [ -n "$served" ] && [ "$(canon "$served")" != "$(canon "$root")" ]; then
ROOT_MATCHES=false
fi
fiIf argument stop:
Windows:
Get-NetTCPConnection -LocalPort 9876 -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force }If install-app (PHASE 5) was run, the Scheduled Task only triggers AtLogOn — unlike macOS's
KeepAlive, there's no mid-session auto-restart to fight, so this kill just works and the server
stays down until the next login. To stop it permanently (not just for this session), disable the
task: Disable-ScheduledTask -TaskName "ClaudeConfigProjectBoard".
macOS:
launchctl bootout "gui/$(id -u)/com.claude-config.project-board" 2>/dev/null
kill $(lsof -ti:9876) 2>/dev/nullIf install-app (PHASE 5) was run, a LaunchAgent restarts the server automatically
(KeepAlive) — plain kill alone would just bounce right back. launchctl bootout unloads the
agent first so the kill actually sticks; it's a no-op (silently fails, ignored) when no agent is
installed, so this line is safe to always run.
Confirm result. If no server was running → report that. Stop here — do not continue to PHASE 2-4.
No argument (default): pick the branch below to ensure the server is running, then continue through PHASE 2, 3, and 4 in order — PHASE 4 (open the app window) always runs, it is not a separate mode.
- SERVER_RUNNING = true && root matches (
ROOT_MATCHES/$rootMatchestrue): jump directly to PHASE 2 — fast path, don't restart (keeps open boards/SSE alive).COLD_START=false. - SERVER_RUNNING = true && root mismatches: a stale server is bound to a different projects root. Kill it (use the
stopkill command above), then start a fresh server with$root. Mention in the report that the server was restarted because the projects root changed.COLD_START=true. - SERVER_RUNNING = false: start the server.
COLD_START=true.
Start the server (mismatch-restart and cold-start both use this):
Windows:
Start-Process -WindowStyle Hidden -FilePath node -ArgumentList "--watch","$env:USERPROFILE\.claude\skills\shared\references\serve-backlog.js","$root" -RedirectStandardOutput "$env:TEMP\backlog-server.log" -RedirectStandardError "$env:TEMP\backlog-server.err"macOS:
nohup node --watch ~/.claude/skills/shared/references/serve-backlog.js "$root" > /tmp/backlog-server.log 2>&1 &Don't block waiting for readiness here — starting it in the background and moving straight on to PHASE 2/3 (both pure filesystem/string logic, no server call) is fine. PHASE 4 shows a loading state in the opened window itself while the server comes up, which is a better experience than a synchronous wait-and-report in chat.
PHASE 2: Show result
Scan projects in the projects root (directories containing a .project/ subdirectory).
Use $root uit PHASE 1 (al resolved).
Windows:
Get-ChildItem -Path $root -Directory | Where-Object { Test-Path "$($_.FullName)\.project" } | Select-Object -ExpandProperty NamemacOS:
for d in "$root"/*/; do [ -d "$d/.project" ] && basename "$d"; doneShow output:
Server: http://localhost:9876
Projects:
- {project-name} → http://localhost:9876/{project-name} (dashboard)
http://localhost:9876/{project-name}/backlog (kanban)
http://localhost:9876/{project-name}/review/{entity} (design review)
- ...If no projects are found, hint toward /project-add or /project-plan.
If AUTO_INSTALLED=true (from PHASE 0), add one line: "Background service wasn't installed yet
— set it up automatically (starts on its own from now on at login)." If it was SKIPPED: <reason>,
add: "Background service auto-install skipped ({reason}) — the server still started normally for
this session; run /project-app install-app once {reason is resolved} to set it up."
PHASE 3: Copy link to clipboard
Determine which URL to copy (context-aware):
- If cwd is directly under the projects root and that subdirectory contains
.project/→ usehttp://localhost:9876/{project-name}(dashboard of current project) - Otherwise → use
http://localhost:9876(server root)
Use $root uit PHASE 1 (al resolved).
Windows (PowerShell):
$cwd = (Get-Location).Path
$url = "http://localhost:9876"
if ($cwd -like "$root\*") {
$rel = $cwd.Substring($root.Length + 1)
$project = $rel.Split('\')[0]
if (Test-Path "$root\$project\.project") { $url = "http://localhost:9876/$project" }
}
Set-Clipboard -Value $url
$urlmacOS (bash):
cwd="$PWD"
url="http://localhost:9876"
if [[ "$cwd" == "$root"/* ]]; then
rel="${cwd#$root/}"
project="${rel%%/*}"
[ -d "$root/$project/.project" ] && url="http://localhost:9876/$project"
fi
printf '%s' "$url" | pbcopy
echo "$url"Show below the projects output:
Link copied: {url}PHASE 4: Open app window
Always runs (unless argument was stop, which already exited above). Reuse $url from PHASE 3
— the same context-aware URL (cwd under a project with .project/ → that project's dashboard;
otherwise the index http://localhost:9876). Use $COLD_START from PHASE 1.
If $COLD_START=true: open a loading state instead of $url directly — the server may take
a second or two to boot. Point the ad-hoc --app= window (or default browser, if no app-capable
browser is found) at the local loading page, which polls the server and redirects itself once
ready:
loading_url="file://$HOME/.claude/skills/shared/references/loading.html?url=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))" "$url")"Use $loading_url in place of $url everywhere below when $COLD_START=true.
macOS — prefer the installed PWA if present (only when $COLD_START=false — the installed
app always opens its fixed start_url, so it can't show the loading page; on a cold start use
the ad-hoc window below instead so the loading page can be shown, even if the PWA is installed).
With install-app (PHASE 5) set up, the LaunchAgent keeps the server warm at all times, so
$COLD_START is false on nearly every run — this installed-PWA branch is the normal path, not
a fallback. $COLD_START=true now only happens without the LaunchAgent (e.g. before running
install-app, or after a manual kill that bypassed stop):
An ad-hoc open -na "Brave Browser" --args --app=... window runs inside that browser's regular
application bundle, so it shares its Dock icon — minimizing it and clicking the Dock icon
later can bring up a plain browser window instead of restoring it, which is confusing. The
installed PWA (via the browser's "Install app" prompt) is a real, separate .app bundle
with its own bundle ID and Dock icon — native minimize/restore, no ambiguity.
Browser preference order is Brave → Chrome → Edge (matches this setup's actual daily
browser — adjust if that changes). A PWA installed in Brave lands in
~/Applications/Brave Browser Apps.localized/, Chrome's in ~/Applications/Chrome
Apps.localized/, Edge's in ~/Applications/Microsoft Edge Apps.localized/ — check all three,
in preference order, since the app could be installed in any of them:
installed_app=""
if [ "$COLD_START" = false ]; then
for appdir in "Brave Browser Apps.localized" "Chrome Apps.localized" "Microsoft Edge Apps.localized"; do
installed_app=$(find "$HOME/Applications/$appdir" -maxdepth 1 -iname "*Project Board*.app" 2>/dev/null | head -1)
[ -n "$installed_app" ] && break
done
fi
if [ -n "$installed_app" ]; then
open -a "$installed_app"
else
target="$url"
[ "$COLD_START" = true ] && target="$loading_url"
opened=false
for app in "Brave Browser" "Google Chrome" "Microsoft Edge"; do
if [ -d "/Applications/$app.app" ]; then
open -na "$app" --args --app="$target"
opened=true
break
fi
done
if [ "$opened" = false ]; then
open "$target"
fi
fiNote: the installed-app path opens the PWA's start_url (the index) — it can't be pointed at
$url directly, since open -a doesn't pass a URL through to an already-scoped app reliably.
That's fine on a warm start: scope is /, so the project's dashboard/backlog is one click away
inside the same app window. If $installed_app is empty (not installed yet, or cold start),
fall back to the --app= window, and mention in the report that installing once (click the
install icon in the address bar on $url) fixes the Dock/minimize behavior for good.
Windows (PowerShell):
$loadingUrl = "file://$env:USERPROFILE\.claude\skills\shared\references\loading.html?url=$([uri]::EscapeDataString($url))"
$target = if ($COLD_START) { $loadingUrl } else { $url }
$candidates = @(
"$env:ProgramFiles\Google\Chrome\Application\chrome.exe",
"${env:ProgramFiles(x86)}\Google\Chrome\Application\chrome.exe",
"$env:ProgramFiles\Microsoft\Edge\Application\msedge.exe",
"${env:ProgramFiles(x86)}\Microsoft\Edge\Application\msedge.exe",
"$env:ProgramFiles\BraveSoftware\Brave-Browser\Application\brave.exe",
"${env:ProgramFiles(x86)}\BraveSoftware\Brave-Browser\Application\brave.exe"
)
$browser = $candidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($browser) {
Start-Process -FilePath $browser -ArgumentList "--app=$target"
} else {
Start-Process $target
}Report:
App window opened: {url}If $COLD_START was true, note that the server was just started and the window shows a loading
state until it's reachable. If $installed_app was used, note that too. Otherwise (ad-hoc
--app= window), add a one-line tip: click "Install app" / the install icon in the browser's
address bar on $url once — that installs a real .app with its own icon and Dock/Launchpad/
Start entry, so minimize/restore works correctly and future /project-app runs auto-detect
and use it. If none of the app-capable browsers were found, note that a normal tab was opened
instead.
PHASE 5: Install app (argument install-app, macOS + Windows)
One-time (or re-run-to-update) setup — after this, /project-app itself is no longer needed
for everyday use; the server is always warm in the background and the installed PWA is the one
Dock/taskbar icon.
macOS:
Todo: Read
.claude/skills/project-app/references/install-app-macos.mdand follow it exactly (LaunchAgent setup, old-launcher removal, PWA install/pin — all as one Bash invocation per step, per the Execution rule above).
Windows:
Todo: Read
.claude/skills/project-app/references/install-app-windows.mdand follow it exactly (Scheduled Task setup, PWA install, taskbar pin — one PowerShell invocation per step, per the Execution rule above). Unlike the macOS doc, the taskbar-pin step is a manual instruction for the user, not a script — Windows blocks programmatic taskbar pinning since Win10.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

