Skip to content
Open
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 @@ -3,6 +3,7 @@
## [Unreleased]

- Added: explicit `app-it` dev-server recipes for Vite + React, SvelteKit, and Astro, including disk detection signals, port behavior, and loopback-only start commands.
- Added: behavioral fixtures `vite-react`, `sveltekit-basic`, and `astro-basic` backing the Vite + React, SvelteKit, and Astro recipes. Each asserts framework detection (via `inspect.sh`) and bundle assembly under `scripts/test-fixtures.sh`, satisfying the recipe-governance rule. Closes #2, #3, #4.
- Fixed: generated run scripts now preserve `$PORT`/`$API_PORT` inside configured start commands until the launcher has selected the runtime ports.
- Changed: hardened the shared Swift `WKWebView` shell against unusable restored window frames by clamping saved frames to the visible display and enforcing a minimum first-launch size.
- Added: internal behavioral **fixture suite** (`scripts/test-fixtures.sh` + `scripts/fixtures/`) that proves app-it works across the project shapes it claims to support. On every push the macOS CI lane drives the *real* scripts — `inspect.sh`, `desktop-build.sh`, the generated launcher, `desktop-doctor.sh`, `desktop-quit.sh` — against tiny shape-fixtures (Vite, Next, static-export, Vite+Express multiserver, hardcoded-port, Chrome-fallback) and asserts the headless-automatable rows of SKILL.md's Phase-4 checklist: build, bundle metadata, no placeholder leak, **runtime port**, server responding, **the server belongs to the launcher** (descendant-walk ownership), warm-reattach, and clean teardown. Hermetic by design — stand-in `$PORT` servers, no framework installs, a sandboxed `HOME`, and trap-based teardown, so it never touches your real `~/Applications`/`~/Library` state. A separate weekly `fixtures-real` lane (and the release checklist) runs one **real** `npm run dev -- --port $PORT` Vite app so framework drift can't silently rot the top recipe. GUI-only checks (window content, Dock icon, Cmd+Q/red-X, LaunchServices) stay a documented manual release smoke.
Expand Down
3 changes: 3 additions & 0 deletions scripts/fixtures/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ For each fixture, `test-fixtures.sh`:
| `vite-basic` | Vite detection; single-server build → launch → port → ownership → warm reattach → teardown; PNG → `.icns` icon round-trip |
| `fixed-port` | `port_mode: "fixed"` uses the exact preferred origin, refuses busy-port fallback with a clear launcher report, and labels foreign preferred-port listeners without touching them |
| `next-basic` | Next detection (PORT-env, no `--port`); bundle assembles for a Next shape |
| `vite-react` | Vite + React detection (the react-specific recipe branch, distinct from `vite-basic`'s plain-Vite shape); bundle assembles for a Vite + React shape |
| `sveltekit-basic` | SvelteKit detection (`svelte.config` + `@sveltejs/kit`/`vite-plugin-svelte` deps); bundle assembles for a SvelteKit shape |
| `astro-basic` | Astro detection (`astro.config` + `astro` dep, port 4321); bundle assembles for an Astro shape |
| `static-export` | app-it-static: static-export detection + serving a prebuilt `out/` with the real stdlib `static-server.py` |
| `vite-express` | A3.2 multiserver template selected; dual-port + `API_PORT`; both ports owned and freed |
| `deep-tree` | the descendant-walk reaches a **gen-2** listener (bash → node → node, like real `npm`/`pnpm` dev) — so warm-reattach and `desktop:doctor` ownership work for real frameworks, not just gen-1 stubs |
Expand Down
16 changes: 16 additions & 0 deletions scripts/fixtures/astro-basic/app-it.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"_comment": "Build-assert fixture (no runtime run). Proves Astro detection (port 4321) and that the bundle assembles. Single-server runtime is covered by vite-basic.",
"apps": [
{
"name": "Astro Basic",
"slug": "astro-basic",
"port": 41120,
"start_command": "node stub-server.js --port $PORT",
"bundle_id": "com.user.astro-basic",
"version": "0.1.0",
"polyfill_path": "",
"backend_port": null,
"backend_start_command": null
}
]
}
3 changes: 3 additions & 0 deletions scripts/fixtures/astro-basic/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Astro shape. Detected by the astro.config.mjs filename plus the astro dep.
// Never imported by the suite (build-assert fixture, no install); kept inert.
export default {};
11 changes: 11 additions & 0 deletions scripts/fixtures/astro-basic/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "astro-basic",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "astro dev"
},
"dependencies": {
"astro": "^4.0.0"
}
}
16 changes: 16 additions & 0 deletions scripts/fixtures/sveltekit-basic/app-it.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"_comment": "Build-assert fixture (no runtime run). Proves SvelteKit detection and that the bundle assembles. Single-server runtime is covered by vite-basic.",
"apps": [
{
"name": "SvelteKit Basic",
"slug": "sveltekit-basic",
"port": 41110,
"start_command": "node stub-server.js --port $PORT",
"bundle_id": "com.user.sveltekit-basic",
"version": "0.1.0",
"polyfill_path": "",
"backend_port": null,
"backend_start_command": null
}
]
}
14 changes: 14 additions & 0 deletions scripts/fixtures/sveltekit-basic/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "sveltekit-basic",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite dev"
},
"devDependencies": {
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^4.0.0",
"svelte": "^5.0.0",
"vite": "^5.0.0"
}
}
5 changes: 5 additions & 0 deletions scripts/fixtures/sveltekit-basic/svelte.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// SvelteKit shape. Detected by the svelte.config.js filename plus the
// @sveltejs/kit + @sveltejs/vite-plugin-svelte + svelte + vite deps. Never
// imported by the suite (build-assert fixture, no install), so its content is
// irrelevant to detection; kept inert on purpose.
export default {};
16 changes: 16 additions & 0 deletions scripts/fixtures/vite-react/app-it.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"_comment": "Build-assert fixture (no runtime run). Proves the Vite + React recipe branch (react-specific; vite-basic's plain-Vite shape does not exercise it) and that the bundle assembles. Single-server runtime is covered by vite-basic.",
"apps": [
{
"name": "Vite React",
"slug": "vite-react",
"port": 41100,
"start_command": "node stub-server.js --port $PORT",
"bundle_id": "com.user.vite-react",
"version": "0.1.0",
"polyfill_path": "",
"backend_port": null,
"backend_start_command": null
}
]
}
16 changes: 16 additions & 0 deletions scripts/fixtures/vite-react/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "vite-react",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite"
},
"dependencies": {
"react": "^18.3.0",
"react-dom": "^18.3.0"
},
"devDependencies": {
"vite": "^5.0.0",
"@vitejs/plugin-react": "^4.3.0"
}
}
8 changes: 8 additions & 0 deletions scripts/fixtures/vite-react/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Vite + React shape. Detected by the vite/react/react-dom/@vitejs/plugin-react
// deps in package.json. Never imported by the suite (build-assert fixture, no
// install), so its content is irrelevant to detection. It deliberately carries
// NO server.port literal, so inspect.sh emits no hardcoded-port warning.
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({ plugins: [react()] })
31 changes: 31 additions & 0 deletions scripts/test-fixtures.sh
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,37 @@ lacks "inspect emits no hardcoded-port warning" "$INSPECT" "hardcoded port liter
build
assert_bundle "Next Basic" next-basic com.user.next-basic swift

# =============================================================================
section "vite-react – Vite + React detection + bundle assembly"
setup_proj vite-react app-it vite-react
INSPECT="$(APP_IT_PROJECT_ROOT="$PROJ" bash "$PROJ/scripts/inspect.sh" 2>&1 || true)"
has "inspect detects vite.config.ts" "$INSPECT" "vite.config.ts"
has "inspect lists the dev script" "$INSPECT" "dev"
has "inspect emits the Vite + React recipe" "$INSPECT" "Vite + React"
lacks "inspect emits no hardcoded-port warning" "$INSPECT" "hardcoded port literal"
build
assert_bundle "Vite React" vite-react com.user.vite-react swift

# =============================================================================
section "sveltekit-basic – SvelteKit detection + bundle assembly"
setup_proj sveltekit-basic app-it sveltekit-basic
INSPECT="$(APP_IT_PROJECT_ROOT="$PROJ" bash "$PROJ/scripts/inspect.sh" 2>&1 || true)"
has "inspect detects svelte.config.js" "$INSPECT" "svelte.config.js"
has "inspect lists the dev script" "$INSPECT" "vite dev"
has "inspect emits the SvelteKit recipe" "$INSPECT" "SvelteKit"
build
assert_bundle "SvelteKit Basic" sveltekit-basic com.user.sveltekit-basic swift

# =============================================================================
section "astro-basic – Astro detection + bundle assembly"
setup_proj astro-basic app-it astro-basic
INSPECT="$(APP_IT_PROJECT_ROOT="$PROJ" bash "$PROJ/scripts/inspect.sh" 2>&1 || true)"
has "inspect detects astro.config.mjs" "$INSPECT" "astro.config.mjs"
has "inspect lists the dev script" "$INSPECT" "astro dev"
has "inspect emits the Astro recipe" "$INSPECT" "Astro"
build
assert_bundle "Astro Basic" astro-basic com.user.astro-basic swift

# =============================================================================
section "claude-artifact-url — URL-only hosted Artifact bundle"
setup_proj claude-artifact-url app-it claude-artifact
Expand Down
Loading