Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## [Unreleased]

- Added: `desktop:doctor` — a self-diagnosing command for generated `app-it` launchers (`scripts/desktop-doctor.sh`). Run `npm run desktop:doctor` long after the build session to get a short, issue-ready report on one launcher: config + placeholder leakage, installed/build `.app`, Info.plist identity, ad-hoc signature, quarantine / iCloud signature-breaking xattrs, preferred-vs-runtime port, stale PID, **whether the process on the runtime port is actually in the recorded supervisor's descendant tree** (reuses the launcher's reattach gate), start-command binary resolution on the launcher's PATH, log/state paths, and **template drift** (feature-probes the installed `wrapper`/`run` against the current templates — no version stamp needed). `--tail[=N]` appends the launcher log. It is a diagnostic, not a fixer: read-only, deterministic, local (no network, no new dependencies), and it says "probably" when a check can't be certain. The opt-in `--fix-safe` flag touches **only app-it's own generated state** — stale pid/port files, this bundle's stale LaunchServices registration, the rebuilt icon, and quarantine on the generated `.app` — never the user's product code, dependencies, config, or anything outside app-it's artifacts. macOS `app-it` plugin only (the `app-it-static` companion has a different runtime model). Embodies Core principle #8 (*runtime truth beats build-time guess*) for end users.
- Added: `app-it-static` companion plugin (`plugins/app-it-static/`) — a macOS sibling of `app-it` for **finished or buildable** apps. Builds once, then serves the built output (`dist/`/`build/`/`out/`/…) from a tiny zero-dependency static server (~15 MB) or directly via `file://` (~0 MB) — **no dev server**, instead of the 300–700 MB a dev server holds. Reuses `app-it`'s native Swift WebKit window, icon pipeline, and one-folder Dock install (the five shared templates are byte-identical and CI guards them against drift). The served output is a snapshot; `desktop:rebuild` refreshes it. Inspired by r/ClaudeAI launch feedback (see README → Community nudge) and recorded in [ADR 0006](docs/decisions/0006-static-companion-snapshot-model.md).
- Added: Windows beta scaffold (`plugins/app-it-windows/`) — a sibling plugin mirroring the macOS contract with Windows primitives (WPF + WebView2 host, PowerShell lifecycle scripts, multi-resolution `.ico`, Start Menu `.lnk`). Build + lint gated by a required `windows-latest` CI job; **untested on real hardware, looking for a maintainer.** See [docs/WINDOWS.md](docs/WINDOWS.md).

Expand Down
33 changes: 33 additions & 0 deletions docs/TROUBLESHOOTING.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
# Troubleshooting

## Diagnose It First

Before anything else, run the doctor on the affected launcher:

```bash
npm run desktop:doctor # read-only health check for one app
npm run desktop:doctor -- --tail # …and show the tail of the launcher log
```

It inspects what app-it actually cares about — config, the installed `.app`,
icon, bundle id, ad-hoc signature, quarantine, the preferred-vs-runtime port,
stale processes, whether the running server really belongs to this launcher, the
log paths, and whether the installed launcher predates the current templates —
and prints a short report you can paste straight into a bug report. It is
read-only and, when it can't be certain, it says "probably" rather than
asserting.

For multi-app projects it diagnoses one launcher at a time; it lists the apps and
defaults to the first, or pass a slug: `npm run desktop:doctor -- <slug>`.

To clean up app-it's **own** generated state — stale PID/port files, a stale
LaunchServices registration, a rebuilt icon, or quarantine on the generated
`.app`:

```bash
npm run desktop:doctor -- --fix-safe
```

`--fix-safe` only ever touches app-it's generated artifacts. It never modifies
your product code, dependencies, framework config, or anything outside app-it's
own output, and it never kills a running server — that is what `desktop:quit`
is for.

## The App Will Not Open

Run the target project's build again:
Expand Down
39 changes: 33 additions & 6 deletions plugins/app-it/skills/app-it/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ description: >-
Cmd+M minimize, Cmd+H hide, Cmd+- / Cmd+= / Cmd+0 page zoom, Cmd+R reload,
Cmd+Ctrl+F toggle full screen, plus standard Edit-menu shortcuts) — these
are wired into `wrapper.swift`'s menu bar, not relied on as AppKit defaults
(the defaults only cover Cmd+Q).
(the defaults only cover Cmd+Q). Generated apps also ship a `desktop:doctor`
command that self-diagnoses one launcher (config, install, signature, ports,
stale PID, server ownership, template drift) read-only, with a narrow
`--fix-safe` that only cleans up app-it's own generated state.
---

# app-it — Make any project launchable from the Dock
Expand Down Expand Up @@ -60,6 +63,7 @@ templates/
desktop-icons.sh # generates AppIcon.icns from a source PNG/SVG
desktop-install.sh # copies bundles to ~/Applications/App It/, refreshes Dock
desktop-quit.sh # stops daemonized servers + wrapper windows
desktop-doctor.sh # self-diagnoses one launcher (read-only; --fix-safe for generated-state cleanup)
inspect.sh # Phase-1 inspection helper (one-shot project probe)
placeholder-icon-gen.sh # last-resort icon generator (SVG via brand tokens)
fsa-polyfill-template.js # File System Access shim (only if needed)
Expand Down Expand Up @@ -143,13 +147,13 @@ Touch as few project files as possible. Allowed additions:
- `assets/<slug>-icon.{png,svg}` per app (or `assets/app-icon.{png,svg}` if single-app).
- `assets/icons/` — generated icon artifacts (gitignore the contents).
- `assets/icons/build/wrapper` — compiled Swift binary (gitignore).
- `scripts/wrapper.swift`, `scripts/run-template*.sh`, `scripts/info-plist-template.xml`, `scripts/desktop-*.sh`, `scripts/inspect.sh`, `scripts/placeholder-icon-gen.sh` — copied verbatim from `templates/`.
- `scripts/wrapper.swift`, `scripts/run-template*.sh`, `scripts/info-plist-template.xml`, `scripts/desktop-*.sh`, `scripts/inspect.sh`, `scripts/placeholder-icon-gen.sh` — copied verbatim from `templates/`. (`scripts/desktop-doctor.sh` is among the `desktop-*.sh` set — see [Diagnosing a generated app](#diagnosing-a-generated-app).)
- `scripts/app-it.config.json` — single source of truth for the APPS list (see below).
- `assets/<slug>-polyfill.js` — only when FSA usage is detected.
- `desktop/<AppName>.app/` per app (gitignore — regenerated by build).
- `docs/desktop-launcher.md`.
- `docs/desktop-launcher.app-it-report.md` — agent decision provenance (see Phase 5).
- `package.json` `scripts` entries: `desktop:build`, `desktop:icons`, `desktop:install`, `desktop:quit`.
- `package.json` `scripts` entries: `desktop:build`, `desktop:icons`, `desktop:install`, `desktop:quit`, `desktop:doctor`.

**Single source of truth: `scripts/app-it.config.json`**

Expand Down Expand Up @@ -534,7 +538,8 @@ Single-app:
"desktop:icons": "APP_NAME='MyApp' APP_SLUG='myapp' ./scripts/desktop-icons.sh",
"desktop:build": "./scripts/desktop-build.sh",
"desktop:install": "./scripts/desktop-install.sh",
"desktop:quit": "./scripts/desktop-quit.sh"
"desktop:quit": "./scripts/desktop-quit.sh",
"desktop:doctor": "./scripts/desktop-doctor.sh"
}
}
```
Expand All @@ -547,11 +552,14 @@ Multi-app (per-app icon variants, aggregate build/install/quit):
"desktop:icons:studio": "APP_NAME='Momo Studio' APP_SLUG='momo-studio' ./scripts/desktop-icons.sh",
"desktop:build": "./scripts/desktop-build.sh",
"desktop:install": "./scripts/desktop-install.sh",
"desktop:quit": "./scripts/desktop-quit.sh"
"desktop:quit": "./scripts/desktop-quit.sh",
"desktop:doctor": "./scripts/desktop-doctor.sh"
}
}
```

For multi-app projects `desktop:doctor` diagnoses one launcher at a time: `npm run desktop:doctor -- <slug>` (it lists the roster and defaults to the first app when no slug is given).

If the project doesn't have `package.json`, expose the same commands via `Makefile` or a top-level shell script.

---
Expand All @@ -569,6 +577,24 @@ For chrome-fallback launchers, document `desktop:quit` as the **primary** shutdo

---

## Diagnosing a generated app

`scripts/desktop-doctor.sh` (wired as `desktop:doctor`) lets a user self-diagnose **one** generated launcher long after the build session ended — no agent required. It is the user-facing embodiment of Core principle #8 (*runtime truth beats build-time guess*): the same `server.port`-first, descendant-walk-ownership, read-the-runtime checks the agent runs in Phase 4, packaged as a command the user can run on their own machine and paste straight into a bug report.

**It is a diagnostic, not a fixer.** Read-only by default. Every check is deterministic and local — no network, no installs, no new dependencies — and when a check can't be certain it says "probably" rather than asserting. It reads `scripts/app-it.config.json` the same way `desktop-build.sh`/`desktop-quit.sh` do, so there is no APPS-table drift.

What it checks: config present + no placeholder leakage; bundle id shape (rejects `com.$(id -un).*`); installed/build `.app` present; Info.plist identity; `run` + Mach-O `wrapper` present; `AppIcon.icns` present; ad-hoc signature; quarantine / iCloud signature-breaking xattrs; preferred-vs-runtime port; stale PID; **whether the process on the runtime port is actually in the recorded supervisor's descendant tree** (reuses the launcher's reattach gate); start-command binary resolves on the launcher's augmented PATH; log/state paths; and **template drift** — it feature-probes the installed `wrapper`/`run` against the current `scripts/wrapper.swift`/`run-template.sh` using the `grep -qboa <marker>` idiom (no version stamp needed). `--tail[=N]` appends the last N lines of `server.log`.

**`--fix-safe`** is the only mutating mode, and it is deliberately narrow — it touches **only app-it's own generated state**, never the user's product code, dependencies, framework config, or anything outside app-it's artifacts:
1. stale PID/port files — removed only when the recorded process is dead;
2. this bundle's stale LaunchServices registration — `lsregister -u` the build-path copy, `-f` the install copy (the same operation `desktop-install.sh` performs);
3. the generated `AppIcon.icns` — rebuilt from the user's source image via `desktop-icons.sh` (mtime-aware), then refreshed into the installed bundle and re-signed;
4. `com.apple.quarantine` on the generated `.app` — cleared with a targeted `xattr -dr` that preserves the signature.

It will never kill a running server (that's `desktop:quit`), never run a package install, and never edit config. Scope it to the `app-it` plugin — the `app-it-static` companion has a different runtime model (no dev-server daemon, no PID/port) and would need its own tailored checks.

---

## Cross-platform notes (only if asked)

**Linux** — `~/.local/share/applications/<slug>.desktop` Desktop Entry; `update-desktop-database`.
Expand Down Expand Up @@ -606,7 +632,7 @@ Picked: "<chosen>". Sources surveyed: <folder>, <package.json name>, <metadata.j
- `assets/<slug>-polyfill.js` per app *(if FSA polyfill needed)*
- `desktop/<AppName>.app/...`
- `scripts/wrapper.swift`, `scripts/run-template*.sh`, `scripts/info-plist-template.xml`
- `scripts/desktop-build.sh`, `scripts/desktop-icons.sh`, `scripts/desktop-install.sh`, `scripts/desktop-quit.sh`
- `scripts/desktop-build.sh`, `scripts/desktop-icons.sh`, `scripts/desktop-install.sh`, `scripts/desktop-quit.sh`, `scripts/desktop-doctor.sh`
- `scripts/inspect.sh`, `scripts/placeholder-icon-gen.sh` *(if used)*
- `scripts/app-it.config.json`
- *(if A3.2)* `vite.config.ts` / `server/index.ts` edits — env-driven ports
Expand All @@ -624,6 +650,7 @@ Replace `assets/<slug>-icon.png`, then `pnpm desktop:icons:<app> && pnpm desktop
- Build: `pnpm desktop:build`
- Install: `pnpm desktop:install` (→ ~/Applications/App It/)
- Quit: `pnpm desktop:quit` (stops daemonized servers)
- Diagnose: `pnpm desktop:doctor` (read-only health check; `-- --fix-safe` for generated-state cleanup, `-- <slug>` to pick an app)

**9. Generated launcher locations:**
- Repo: `desktop/<AppName>.app`
Expand Down
Loading
Loading