Skip to content

Commit 60f2fee

Browse files
Merge pull request #15 from cozystack/docs/dev-ui-bootstrap-in-tree
docs(dev-ui-bootstrap): retarget skill to the in-tree console
2 parents b4a487f + c7cb95f commit 60f2fee

1 file changed

Lines changed: 45 additions & 36 deletions

File tree

  • plugins/cozystack/skills/dev-ui-bootstrap
Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
---
22
name: dev-ui-bootstrap
3-
description: Bootstrap a UI dev sandbox — Playwright end-to-end testing scaffolding for the cozystack-ui SPA plus a Vite dev server connected to a chosen Kubernetes cluster. Use whenever the operator wants to fix, debug, screenshot, or write Playwright tests against the Cozystack console — e.g. "prepare cozystack-ui for a fix", "spin up the UI against my dev cluster", "install playwright in cozystack-ui", "give me a UI sandbox pointing at <kubeconfig>". Handles worktree creation, @playwright/test + Chromium install, playwright.config.ts, dev:e2e / test:e2e scripts, kubectl proxy against a kubeconfig, and a Vite server on a free port so the operator can immediately start poking at the running app.
3+
description: Bootstrap a UI dev sandbox — Playwright end-to-end testing scaffolding for the Cozystack console UI (vendored in the monorepo at `packages/system/dashboard/images/console`) plus a Vite dev server connected to a chosen Kubernetes cluster. Use whenever the operator wants to fix, debug, screenshot, or write Playwright tests against the Cozystack console — e.g. "prepare the console UI for a fix", "spin up cozystack-ui against my dev cluster", "install playwright in the console", "give me a UI sandbox pointing at <kubeconfig>". Handles monorepo worktree creation, @playwright/test + Chromium install, playwright.config.ts, dev:e2e / test:e2e scripts, kubectl proxy against a kubeconfig, and a Vite server on a free port so the operator can immediately start poking at the running app.
44
argument-hint: "[--kubeconfig=<path>] [--worktree=<name>] [--port=<n>] [--proxy-port=<n>] [--no-install] [--no-browser]"
55
---
66

77
# cozystack:dev-ui-bootstrap
88

9-
This skill puts an operator in front of a running, working copy of `cozystack-ui` with Playwright wired up, pointed at a real Kubernetes cluster of their choosing. It is the standard entry point for "I want to fix something in the UI" tasks — every step is reversible and avoids touching the repo's main worktree.
9+
This skill puts an operator in front of a running copy of the Cozystack console UI with Playwright wired up, pointed at a real Kubernetes cluster of their choosing. The UI is vendored in-tree in the `cozystack/cozystack` monorepo at `packages/system/dashboard/images/console` (the standalone `cozystack-ui` repo is archived). It is the standard entry point for "I want to fix something in the UI" tasks — every step is reversible and avoids touching the monorepo's main worktree.
1010

1111
Work in reasoning mode. Walk through the phases in order. State which phase you are in as you go, so the operator can interrupt early if a default is wrong for their machine.
1212

@@ -19,60 +19,67 @@ Match the operator's natural language detected from prior conversation messages
1919
`$ARGUMENTS` is the free-form tail after `/cozystack:dev-ui-bootstrap`. Extract:
2020

2121
- `--kubeconfig=<path>` — kubeconfig the Vite dev server should hit through `kubectl proxy`. If absent: list `~/.kube/*config*` files via `ls`, then `AskUserQuestion` to pick one, defaulting to `~/.kube/config` (or `$KUBECONFIG` if set). Never assume `~/.kube/config` silently — picking the wrong cluster is a frequent footgun.
22-
- `--worktree=<name>` — name (relative to `<repo>/.claude/worktrees/`) for the throwaway worktree. Default: `fix-app` (matches the in-tree convention).
23-
- `--port=<n>` — Vite dev port. Default: pick the first free port starting at `3002` (since `3001` is the repo default and is often already in use by another session).
24-
- `--proxy-port=<n>``kubectl proxy` port. Default: `8001` (matches `apps/console/vite.config.ts`). If already in use by a proxy pointing at a *different* kubeconfig, pick the next free port (see Phase 7 for the Vite-side consequence).
22+
- `--worktree=<name>` — name (relative to `<repo>/.claude/worktrees/`) for the throwaway monorepo worktree. Default: `fix-app`.
23+
- `--port=<n>` — Vite dev port. Default: pick the first free port starting at `3002` (since `3001` is the console default and is often already in use by another session).
24+
- `--proxy-port=<n>``kubectl proxy` port. Default: `8001` (matches the console's `apps/console/vite.config.ts`). If already in use by a proxy pointing at a *different* kubeconfig, pick the next free port (see Phase 7 for the Vite-side consequence).
2525
- `--no-install` — skip `pnpm install` and `@playwright/test` install if they are already present.
2626
- `--no-browser` — skip `npx playwright install chromium`.
2727

28-
## Phase 2 — Locate the cozystack-ui repo
28+
## Phase 2 — Locate the console workspace (in-tree in the monorepo)
2929

30-
Resolve in priority order:
30+
The console UI is a pnpm workspace inside the monorepo, not a standalone repo. Resolve two paths:
3131

32-
1. If `pwd` is inside a checkout of `cozystack-ui` (contains `apps/console/package.json` with `"name": "@cozystack/console"`) → use the repo root from `git rev-parse --show-toplevel`.
33-
2. `~/aenix/cozystack-ui` — the canonical clone on contributors' workstations.
34-
3. Otherwise — `AskUserQuestion` for the path. Do not `git clone`; the skill is for an already-cloned working copy.
32+
- `$REPO` — the `cozystack/cozystack` monorepo root (git / worktree operations happen here).
33+
- `$CONSOLE` — the workspace root, `$REPO/packages/system/dashboard/images/console` (all `pnpm` / `vite` / `playwright` / file edits happen here; it holds `pnpm-workspace.yaml`, the root `package.json`, `pnpm-lock.yaml`, and `apps/console`).
3534

36-
Verify `pnpm` and `kubectl` are on `$PATH`, and that the workspace's `package.json` has `"packageManager": "pnpm@..."`. If either binary is missing, stop and tell the operator to install it — do not auto-install package managers or CLIs. Catching this here avoids failing five phases deeper when `kubectl proxy` is first invoked.
35+
Resolve `$REPO` in priority order:
36+
37+
1. If `pwd` is inside a cozystack monorepo checkout whose `<toplevel>/packages/system/dashboard/images/console/apps/console/package.json` has `"name": "@cozystack/console"` → use the toplevel from `git rev-parse --show-toplevel`.
38+
2. `~/aenix/cozystack` — the canonical monorepo clone on contributors' workstations.
39+
3. Otherwise — `AskUserQuestion` for the monorepo path. Do not `git clone`; the skill is for an already-cloned working copy.
40+
41+
> The console UI used to live in a standalone `cozystack-ui` repo. It was vendored into the monorepo (cozystack/cozystack#2963) and that repo is archived — there is no `~/aenix/cozystack-ui` to use anymore.
42+
43+
Verify `pnpm` and `kubectl` are on `$PATH`, and that `$CONSOLE/package.json` has `"packageManager": "pnpm@..."`. If either binary is missing, stop and tell the operator to install it — do not auto-install package managers or CLIs. Catching this here avoids failing five phases deeper when `kubectl proxy` is first invoked.
3744

3845
## Phase 3 — Worktree creation
3946

40-
Create a feature worktree off `main` for the upcoming fix work. The repo's CLAUDE.md is emphatic that the primary working directory must stay on its default branch — multiple Claude sessions share it.
47+
Create a feature worktree of the **monorepo** off `main` for the upcoming fix work. The user's CLAUDE.md is emphatic that the primary working directory must stay on its default branch — multiple Claude sessions share it.
4148

4249
```bash
43-
git -C <repo> worktree add <repo>/.claude/worktrees/<name> -b fix/<name> main
50+
git -C $REPO worktree add $REPO/.claude/worktrees/<name> -b fix/<name> main
4451
```
4552

4653
If a worktree at that path already exists:
4754

4855
- If clean and on the expected branch → reuse it; no `git worktree add`.
4956
- If dirty or on a different branch → `AskUserQuestion`: reuse / pick a new name / cancel.
5057

51-
Set `$WT` to the absolute worktree path. Every subsequent command in this skill runs from `$WT`.
58+
Set `$WT` to the absolute worktree path, and re-point `$CONSOLE` into it: `$CONSOLE="$WT/packages/system/dashboard/images/console"`. Every subsequent `pnpm` / `vite` / `playwright` / file command in this skill runs from `$CONSOLE`; only `git` and worktree operations use `$REPO` / `$WT`.
5259

5360
## Phase 4 — Install dependencies
5461

55-
Always run `pnpm install` once in `$WT` (fast no-op if the lockfile is satisfied) — workspace symlinks must exist before Playwright lands.
62+
Always run `pnpm install` once in `$CONSOLE` (fast no-op if the lockfile is satisfied) — workspace symlinks must exist before Playwright lands. (Run it from `$CONSOLE`, not `$WT`: the monorepo root is not a pnpm workspace; the console subdirectory is.)
5663

5764
Then, unless `--no-install`:
5865

5966
```bash
60-
pnpm --filter @cozystack/console add -D @playwright/test
67+
cd $CONSOLE && pnpm --filter @cozystack/console add -D @playwright/test
6168
```
6269

6370
Unless `--no-browser`:
6471

6572
```bash
66-
cd $WT/apps/console && pnpm exec playwright install chromium
73+
cd $CONSOLE/apps/console && pnpm exec playwright install chromium
6774
```
6875

6976
Chromium is ~120 MB. Note the size in the running commentary so an operator on a slow link knows what is happening. The download lives in `~/.cache/ms-playwright/` and is shared across worktrees — usually a one-time cost.
7077

7178
## Phase 5 — Write Playwright config + scripts
7279

73-
Idempotency: if `apps/console/playwright.config.ts` already exists, diff against the template below and ask before overwriting — the operator may have customised it.
80+
Idempotency: if `$CONSOLE/apps/console/playwright.config.ts` already exists, diff against the template below and ask before overwriting — the operator may have customised it.
7481

75-
Write `$WT/apps/console/playwright.config.ts`:
82+
Write `$CONSOLE/apps/console/playwright.config.ts`:
7683

7784
```ts
7885
import { defineConfig, devices } from "@playwright/test"
@@ -97,14 +104,14 @@ export default defineConfig({
97104

98105
Replace `<PORT>` with the resolved Vite dev port. The `E2E_BASE_URL` env-var override lets CI or another session point Playwright at a different running instance.
99106

100-
Create `$WT/apps/console/e2e/` (empty — tests land here as the operator writes them).
107+
Create `$CONSOLE/apps/console/e2e/` (empty — tests land here as the operator writes them).
101108

102-
Patch `$WT/apps/console/package.json` `"scripts"` with `Edit` — preserve unrelated keys, do not rewrite the whole file:
109+
Patch `$CONSOLE/apps/console/package.json` `"scripts"` with `Edit` — preserve unrelated keys, do not rewrite the whole file:
103110

104111
- Add `"dev:e2e": "vite --port <PORT>"` next to `"dev"`.
105112
- Add `"test:e2e": "playwright test"` next to `"test"`.
106113

107-
No semicolons in the config file — the repo's CLAUDE.md is strict: every file in the tree is semicolon-free, and reformatting unrelated files is forbidden.
114+
No semicolons in the config file — the console's `CLAUDE.md` is strict: every file in the tree is semicolon-free, and reformatting unrelated files is forbidden.
108115

109116
## Phase 6 — Bring up `kubectl proxy`
110117

@@ -119,53 +126,55 @@ Goal: a `kubectl proxy` instance is reachable on `$PROXY_PORT` and is talking to
119126
```
120127
4. Smoke-test with `curl -sS -o /dev/null -w "%{http_code}" http://localhost:$PROXY_PORT/api`. Expect `200`. `/api` is the unauthenticated API-group discovery endpoint — it returns `200` from any reachable apiserver and avoids false negatives from RBAC-restricted kubeconfigs that cannot `get namespaces/default`. On `401`/`403` the kubeconfig is wrong or expired — surface the error and stop. On connection refused, wait up to 5 s for the proxy to come up before failing.
121128

122-
Capture the proxy process ID and the kubeconfig path in a marker file at `$WT/.cozystack-dev-ui-bootstrap-proxy` so a follow-up session knows what is running. Never write secrets there — kubeconfig path and context name only.
129+
Capture the proxy process ID and the kubeconfig path in a marker file at `$CONSOLE/.cozystack-dev-ui-bootstrap-proxy` so a follow-up session knows what is running. Never write secrets there — kubeconfig path and context name only.
123130

124131
## Phase 7 — Start the Vite dev server
125132

126-
If `$PROXY_PORT` is non-default (8001), the dev server still hard-codes `http://localhost:8001` in `vite.config.ts`. Two options:
133+
If `$PROXY_PORT` is non-default (8001), the dev server still hard-codes `http://localhost:8001` in `apps/console/vite.config.ts`. Two options:
127134

128-
- Preferred: keep the default proxy port `8001` and explain why. The repo treats `vite.config.ts` as load-bearing and edits there are best left for a separate PR.
135+
- Preferred: keep the default proxy port `8001` and explain why. The console treats `vite.config.ts` as load-bearing and edits there are best left for a separate PR.
129136
- If the operator explicitly said "change the proxy port", `AskUserQuestion` before touching `vite.config.ts`.
130137

131-
Start the dev server in the background, redirecting output to a log file under `$WT` so the polling loop has something to grep:
138+
Start the dev server in the background, redirecting output to a log file under `$CONSOLE` so the polling loop has something to grep:
132139

133140
```bash
134-
cd $WT && pnpm --filter @cozystack/console dev:e2e \
135-
>$WT/.cozystack-dev-ui-bootstrap-vite.log 2>&1 &
141+
cd $CONSOLE && pnpm --filter @cozystack/console dev:e2e \
142+
>$CONSOLE/.cozystack-dev-ui-bootstrap-vite.log 2>&1 &
136143
```
137144

138-
Wait for the `ready in` / `Local:` line via a polling loop against that log file (`until grep -q "ready in" $WT/.cozystack-dev-ui-bootstrap-vite.log; do sleep 0.5; done`, with a 30 s timeout guard). Then `curl -sS -o /dev/null -w "%{http_code}" http://localhost:$PORT/` and expect `200`.
145+
Wait for the `ready in` / `Local:` line via a polling loop against that log file (`until grep -q "ready in" $CONSOLE/.cozystack-dev-ui-bootstrap-vite.log; do sleep 0.5; done`, with a 30 s timeout guard). Then `curl -sS -o /dev/null -w "%{http_code}" http://localhost:$PORT/` and expect `200`.
139146

140147
If the server fails to bind (port already in use after the free-port scan), the cause is usually a stale `vite` from a prior crashed session. Run `lsof -nP -iTCP:$PORT -sTCP:LISTEN` and show the result — let the operator decide whether to kill it.
141148

142149
## Phase 8 — Summary
143150

144151
Print a compact handoff:
145152

146-
- Worktree: `$WT` (branch `fix/<name>`)
147-
- Repo state: clean / dirty (one-line `git status -s`)
153+
- Monorepo worktree: `$WT` (branch `fix/<name>`); console workspace at `$CONSOLE`
154+
- Repo state: clean / dirty (one-line `git -C $WT status -s`)
148155
- Kubeconfig: `<path>` → cluster context `<name>` (resolved in Phase 6)
149156
- `kubectl proxy`: `pid <n>` on `http://localhost:$PROXY_PORT` (smoke `200`)
150157
- Vite dev: `pid <n>` on `http://localhost:$PORT` (smoke `200`)
151-
- Playwright: configured at `apps/console/playwright.config.ts`, run with `pnpm --filter @cozystack/console test:e2e`
158+
- Playwright: configured at `$CONSOLE/apps/console/playwright.config.ts`, run with `pnpm --filter @cozystack/console test:e2e` from `$CONSOLE`
152159
- Next steps the operator can take, e.g.: "Tell me what to fix, or paste a Playwright test idea and I will draft it."
153160

154161
Do **not** open the browser. Leave that to the operator — the dev server is reachable and the operator may want to attach an authenticated session, devtools, or a screen recorder of their own choice.
155162

156163
## Guardrails
157164

158-
- **Never** run `git checkout` in the primary `cozystack-ui` working tree. All branch work happens inside the worktree from Phase 3.
159-
- **Never** edit files in the primary working tree of `cozystack-ui` — only inside `$WT`.
165+
- **Never** run `git checkout` in the primary cozystack monorepo working tree. All branch work happens inside the worktree from Phase 3.
166+
- **Never** edit files in the primary monorepo working tree — only inside `$WT` / `$CONSOLE`.
160167
- **Never** commit, push, or open a PR as part of this skill. The skill *prepares* the environment; the fix itself is a separate, operator-driven step that goes through the normal commit / PR gates.
161168
- **Never** start a `kubectl proxy` with a kubeconfig the operator did not explicitly pick. If `--kubeconfig` is absent, ask.
162169
- **Never** kill a process the operator did not authorise. If a port is occupied, show what is on it and stop.
163-
- **Never** add a new top-level dependency to the cozystack-ui workspace other than `@playwright/test`. The repo's CLAUDE.md is strict about bundle size; Playwright is dev-only and acceptable.
170+
- **Never** add a new top-level dependency to the console workspace other than `@playwright/test`. The console's `CLAUDE.md` is strict about bundle size; Playwright is dev-only and acceptable.
164171
- Refuse to run against a kubeconfig whose current-context name contains `prod` unless the operator re-confirms with `AskUserQuestion`. The console is read/write — pointing it at production is a deploy-shaped risk.
165172

166173
## References
167174

175+
All paths below are relative to `$CONSOLE` (`packages/system/dashboard/images/console` inside the monorepo):
176+
168177
- `apps/console/vite.config.ts` — the existing dev server proxy contract (`/api`, `/apis`, `/k8s``http://localhost:8001`).
169178
- `apps/console/package.json` — where the new `dev:e2e` and `test:e2e` scripts land.
170-
- `CLAUDE.md` (repo root) — code style (no semicolons, `.ts` extensions in imports, no `any`, etc.) and the worktree mandate.
179+
- `CLAUDE.md` (at `$CONSOLE`) — code style (no semicolons, `.ts` extensions in imports, no `any`, etc.). The monorepo root `AGENTS.md` covers the broader worktree mandate.
171180
- `~/.kube/` — separate kubeconfigs per cluster (this host uses one-file-per-cluster, not a merged config).

0 commit comments

Comments
 (0)