# Add PWA Support to an Angular App

Yes. An agent adds the Angular service worker and a web app manifest to a standalone Angular app, caches the fonts it loads, builds it, and opens a pull request.

[Run this on Rush](https://cloud.getrush.ai/run/add-pwa-support/angular/), in a Project that has your repository.

## What the agent does

The agent reads the repository before it runs a schematic. In an app shaped like realworld-apps/angular-realworld-example-app, `package.json` pins every `@angular/*` package to 21.2.12 and `@angular/cli` to 21.2.10, the lockfile is `bun.lock`, and the scripts are `bun run start`, `bun run build`, `bun run test` on Vitest 4 through `@analogjs/vite-plugin-angular`, and `bun run test:e2e` on Playwright. `CLAUDE.md` says to run `bun run setup` first, because the theme stylesheet and the end-to-end specs live in the `realworld` git submodule, and to run `bun run format` before presenting code. `angular.json` has one project, `angular-conduit`, built with `@angular/build:application` into `dist/angular-conduit`, with an explicit `assets` list rather than a `public` folder, a production budget of 500 kB warning and 1 MB error on the initial bundle, and `src/_redirects` copied to the output root.

The bootstrap is standalone. `src/main.ts` calls `bootstrapApplication(AppComponent, appConfig)`, and `src/app/app.config.ts` provides zoneless change detection, the router, an `HttpClient` with the `api`, `token` and `error` interceptors, and an app initializer that checks the stored JWT. `src/index.html` loads Ionicons and Google Fonts from external hosts. These are the places the PWA work touches: the service worker is registered as a provider in `app.config.ts`, and the manifest link and `theme-color` meta go into `index.html`.

The agent runs `bunx ng add @angular/pwa@21 --skip-confirmation`, pinning the schematic to the 21 line because `@angular/pwa` 21.2 asks for `@angular/cli` 21.2.10 or newer and the latest 22 line asks for CLI 22. The schematic adds `@angular/service-worker` at 21.2.12, which must match `@angular/core` exactly, writes `ngsw-config.json` and `manifest.webmanifest` with an icon set, adds `provideServiceWorker('ngsw-worker.js', { enabled: !isDevMode(), registrationStrategy: 'registerWhenStable:30000' })` to the providers, and sets `serviceWorker` in the build options. Because this project lists assets explicitly, the agent checks the entries the schematic wrote against the existing `assets` array and the `_redirects` entry rather than assuming a `public` directory. It then adds the Google Fonts and Ionicons origins to an `assetGroups` entry in `ngsw-config.json` so the app shell renders offline with its real typefaces.

Updates are handled, not just installed. A small `src/app/core/pwa/update.service.ts` subscribes to `SwUpdate.versionUpdates`, and on `VERSION_READY` prompts for a reload; its spec under `src/**/*.spec.ts`, which `vitest.config.ts` includes, mocks `SwUpdate` with an observable and asserts the prompt fires once. The agent runs `bun run build` with the production configuration and confirms `dist/angular-conduit/browser/ngsw.json` and the manifest are in the output, serves that directory and loads it in the sandbox browser to confirm the worker registers, then runs `bun run test`, `bun run test:e2e` and `bun run format:check`.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Make this Angular app installable as a PWA. Read package.json, bun.lock, CLAUDE.md, angular.json, src/index.html, src/main.ts and src/app/app.config.ts first, and run bun run setup so the realworld submodule is present. Add @angular/pwa with the schematic pinned to the same major as @angular/cli, check what it wrote into angular.json against the explicit assets list, cache the external font and icon stylesheets in ngsw-config.json, add an update service that prompts to reload on VERSION_READY with a Vitest spec, build for production and confirm ngsw.json is in dist/angular-conduit/browser, serve the build and confirm the service worker registers, run bun run test, bun run test:e2e and bun run format:check, and open a pull request.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

What comes back is a pull request on its own branch. The diff adds `ngsw-config.json`, the manifest and its icons, and `src/app/core/pwa/update.service.ts` with its spec; it changes `package.json` and `bun.lock` for `@angular/service-worker`, `angular.json` for the `serviceWorker` option and any asset entries, `src/index.html` for the manifest link and `theme-color`, and `src/app/app.config.ts` for the provider. The description lists the cached origins, states the registration strategy, and names the production bundle size the build printed against the 500 kB budget.

The checks are the proof. The `Format Check` workflow runs `bun run format:check`, the `Playwright Tests` workflow runs the RealWorld end-to-end suite against the dev server, and the `Build and Release` workflow on main zips `dist/angular-conduit/browser`, so `ngsw.json` ships in the same artifact. The session's trajectory at `GET /o/{org}/sessions/{id}/messages` keeps the `ng add` output, the production build and every test run.

Before merging, a reviewer checks that `@angular/service-worker` matches `@angular/core` at 21.2.12, that the worker is disabled in development so `bun run start` is unaffected, and that the API host is not in an asset group, since cached API responses would serve stale articles. Nothing reaches main until the pull request merges, and the worker only takes effect on the deployed build.

## Run it on every new Angular repository

Start one run per frontend repository that has no service worker from your own server: `POST /o/{org}/sessions` with this prompt and that repository in `resources`, one call each. `GET /o/{org}/github` lists every repository the Rush GitHub App installation can reach, so a script can find the ones with an `angular.json` and no `ngsw-config.json` and start a run for each. Each run has its own sandbox and its own branch, and a `request_id` per repository makes a retried call return the run it already started.

Give the key a `webhook_url` and each run POSTs `run.completed` with its `session_url` when the turn finishes, so the pull request links collect on your side without polling. A key has at most `max_concurrent` runs in progress at once, 4 by default, and a further start is `429`, so a batch over many repositories should feed them in as earlier ones finish.

## Why run it on Rush?

A PWA is only done when the production build registers a worker and the app shell loads with the network off, and that check is awkward on a laptop: the worker is disabled in development, so you build, serve the output on another port, open devtools, toggle offline and reload, then do it again after every change. On Rush the agent does that loop in its own sandbox with its own browser, and each build and each page load is in the transcript.

The hand-off is also the proof. The pull request carries the config, the provider, the update service and its spec; the repository's Playwright and format workflows run on it; and the trajectory shows the production build's bundle sizes and the worker registering. Several repositories mean several sandboxes at once, each with its own pull request, rather than a sequence of afternoons.

## Frequently asked questions

### Why pin the schematic to version 21?

Because `@angular/pwa` on the 22 line declares a peer dependency on `@angular/cli` 22, and this repository is on CLI 21.2.10 with every framework package at 21.2.12. The 21.2 line of the schematic asks for CLI 21.2.10 or newer and adds `@angular/service-worker` at the version that matches `@angular/core` exactly, which the service worker package requires.

### Will the service worker run during bun run start?

No. The provider is registered with `enabled: !isDevMode()`, so the worker is active only in production builds. The Playwright suite runs against the dev server, as `playwright.config.ts` starts the dev server on port 4200, so it keeps testing the app without a worker in the way.

### What gets cached offline?

The app shell the schematic lists in `ngsw-config.json`: `index.html`, the built scripts and styles, the manifest and the assets, plus the Google Fonts and Ionicons stylesheets from `src/index.html`, which the agent adds as external URLs in an asset group. API responses are not cached, so article lists and the signed-in user always come from the network.

### What about the explicit assets list in angular.json?

This project has no `public` directory; `angular.json` lists `src/favicon.ico`, the SVGs from the `realworld` submodule and `src/_redirects` one by one. The agent reads what the schematic wrote for the manifest and icons and reconciles it with that list before building, and the pull request shows the final `assets` array so a reviewer can see nothing was dropped.

### How do users get a new version?

The worker fetches `ngsw.json` on navigation and downloads a changed build in the background. The added `update.service.ts` listens on `SwUpdate.versionUpdates` and, on `VERSION_READY`, asks the user to reload; its spec mocks `SwUpdate` and asserts the prompt. With `registerWhenStable:30000` the worker registers once the app is stable or after 30 seconds, whichever comes first.
