From 900771cd083763f39c4dfe1abd5b92c564a23ad4 Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Tue, 19 May 2026 09:46:34 +0500 Subject: [PATCH 1/6] feat(dev-ui-bootstrap): new skill for Playwright + Vite sandbox on cozystack-ui Bootstraps a UI dev sandbox: brings up a feature worktree, installs @playwright/test + Chromium, writes playwright.config.ts, adds dev:e2e / test:e2e scripts, starts kubectl proxy against a chosen kubeconfig, and runs Vite on a free port. Lets an operator land in front of a running cozystack-ui pointed at any cluster without touching the repo's primary working tree. Named dev-ui-bootstrap (not ui-e2e) to follow the area-verb pattern used by sibling skills in the new scheme (talos-bootstrap, ubuntu-bootstrap, cluster-install, package-deploy, ...). Co-Authored-By: Claude Opus 4.7 Signed-off-by: Myasnikov Daniil --- .claude-plugin/marketplace.json | 2 +- README.md | 6 +- plugins/cozystack/.claude-plugin/plugin.json | 4 +- .../skills/dev-ui-bootstrap/SKILL.md | 170 ++++++++++++++++++ 4 files changed, 177 insertions(+), 5 deletions(-) create mode 100644 plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 3544692..279af82 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -9,7 +9,7 @@ "plugins": [ { "name": "cozystack", - "description": "Cozystack platform skills bundle — wizard (entry-point orchestrator that interviews + dispatches the chain), talos-bootstrap (Talos node prep via talm with maintenance-mode probe, cert-SAN NAT guardrail, multidoc machine-config, and opt-in boot-method picker), talos-reset (cloud-provider terminate+relaunch helper for unrecoverable Talos nodes — OCI/AWS/GCP/Hetzner; preserves disks + VNICs + NSGs), ubuntu-bootstrap (Ubuntu/Debian k3s bootstrap wrapping ansible-cozystack), cluster-install (Cozystack on a ready cluster — node-readiness, ZFS pool provisioning, extractedprism HA proxy, all-HRs-Ready + storage-pools-registered gate), debug (investigate stuck installs — classify operator-error/config-drift/upstream-bug/not-supported, apply fixes or workarounds, draft upstream issues on approval), cluster-upgrade (release-notes-driven v1.x patch/minor upgrade), package-deploy (dev-loop deploy with ExternalArtifact support), package-bump (single-package version bump with changelog adaptation), external-app-create (scaffold a new external-apps package). Invoked as cozystack:wizard, cozystack:talos-bootstrap, cozystack:talos-reset, cozystack:ubuntu-bootstrap, cozystack:cluster-install, cozystack:debug, cozystack:cluster-upgrade, cozystack:package-deploy, cozystack:package-bump, cozystack:external-app-create.", + "description": "Cozystack platform skills bundle — wizard (entry-point orchestrator that interviews + dispatches the chain), talos-bootstrap (Talos node prep via talm with maintenance-mode probe, cert-SAN NAT guardrail, multidoc machine-config, and opt-in boot-method picker), talos-reset (cloud-provider terminate+relaunch helper for unrecoverable Talos nodes — OCI/AWS/GCP/Hetzner; preserves disks + VNICs + NSGs), ubuntu-bootstrap (Ubuntu/Debian k3s bootstrap wrapping ansible-cozystack), cluster-install (Cozystack on a ready cluster — node-readiness, ZFS pool provisioning, extractedprism HA proxy, all-HRs-Ready + storage-pools-registered gate), debug (investigate stuck installs — classify operator-error/config-drift/upstream-bug/not-supported, apply fixes or workarounds, draft upstream issues on approval), cluster-upgrade (release-notes-driven v1.x patch/minor upgrade), package-deploy (dev-loop deploy with ExternalArtifact support), package-bump (single-package version bump with changelog adaptation), external-app-create (scaffold a new external-apps package), dev-ui-bootstrap (Playwright + Vite dev server scaffolding for cozystack-ui pointed at a chosen kubeconfig — worktree, @playwright/test, Chromium, kubectl proxy + Vite on a free port). Invoked as cozystack:wizard, cozystack:talos-bootstrap, cozystack:talos-reset, cozystack:ubuntu-bootstrap, cozystack:cluster-install, cozystack:debug, cozystack:cluster-upgrade, cozystack:package-deploy, cozystack:package-bump, cozystack:external-app-create, cozystack:dev-ui-bootstrap.", "source": "./plugins/cozystack", "category": "infrastructure" }, diff --git a/README.md b/README.md index be9bf3a..8e42c2d 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Install a plugin: ### cozystack -Platform skills bundle. One install gives you nine skills, invoked as `/cozystack:`. Start with `/cozystack:wizard` — it asks Talos / Ubuntu / Existing and picks the chain. +Platform skills bundle. One install gives you eleven skills, invoked as `/cozystack:`. Start with `/cozystack:wizard` — it asks Talos / Ubuntu / Existing and picks the chain. | Skill | Description | | --- | --- | @@ -36,6 +36,7 @@ Platform skills bundle. One install gives you nine skills, invoked as `/cozystac | **/cozystack:package-deploy** | Deploy a single Cozystack package to a dev cluster via make + cozyhr — handles fresh install and dev-loop iteration with ExternalArtifact support. | | **/cozystack:package-bump** | Bump a single package inside the cozystack monorepo — reads upstream changelog, adapts to breaking changes, regenerates schema, optionally deploys to a dev cluster. | | **/cozystack:external-app-create** | Scaffold a new Cozystack external app package with dependency integration (managed CNPG Postgres, external secret references). | +| **/cozystack:dev-ui-bootstrap** | Bootstrap a UI dev sandbox — Playwright + Vite dev server for `cozystack-ui` pointed at a chosen kubeconfig. Creates a feature worktree, installs `@playwright/test` and Chromium, writes `playwright.config.ts`, adds `dev:e2e` / `test:e2e` scripts, and brings up `kubectl proxy` + Vite on a free port. Use when fixing, debugging, or writing tests against the Cozystack console. | Chains the wizard builds: @@ -70,7 +71,7 @@ Operators can opt out with `--no-extractedprism` and supply their own `--api-hos ```text plugins/ - cozystack/ # platform bundle (9 skills) + cozystack/ # platform bundle (11 skills) .claude-plugin/plugin.json skills/ wizard/ # entry point: interview + chain dispatcher @@ -83,6 +84,7 @@ plugins/ package-deploy/ # dev-loop deploy of a single package package-bump/ # bump a monorepo package external-app-create/ # scaffold a new external-apps package + dev-ui-bootstrap/ # bootstrap Playwright + Vite sandbox for cozystack-ui linstor/ # storage bundle (1 skill) .claude-plugin/plugin.json skills/ diff --git a/plugins/cozystack/.claude-plugin/plugin.json b/plugins/cozystack/.claude-plugin/plugin.json index a105b3e..2215e6b 100644 --- a/plugins/cozystack/.claude-plugin/plugin.json +++ b/plugins/cozystack/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "cozystack", - "version": "1.14.1", - "description": "Cozystack platform skills bundle. Start with cozystack:wizard — it begins with a free-form 'tell me about your setup and goal' question, parses hints, then asks Talos / Ubuntu / Existing, builds a chain, and dispatches downstream skills via a cluster config directory the operator picks (every artifact lives there: inventory.yml, kubeconfig, .state.yaml, cozystack-platform-package.yaml — operator manages git on their own; optional sops opt-in encrypts secret files in-tree). Skills, invoked as cozystack:: wizard (orchestrator + 3-route dispatcher + Phase 4.5 active research + auto-dispatches debug on any failed_at), talos-bootstrap (Talos node prep via talm — Talos-1.12-aware maintenance probe, NAT-provider cert-SAN guardrail before first talm apply, multidoc machine-config with per-node VIP-link IPv4 stubs, etcd bootstrap, kubeconfig fetch, cozystack-tuned shape verification with Phase 11.5 auto-upgrade), talos-reset (cloud-provider terminate+relaunch helper for OCI/AWS/GCP/Hetzner when nodes are unrecoverable from inside; preserves block volumes + secondary VNICs + NSG memberships), ubuntu-bootstrap (wraps cozystack/ansible-cozystack — OS prep + k3s install in one go), cluster-install (Cozystack on a ready cluster — node-readiness, ZFS pool provisioning via privileged DaemonSet on Talos with hostNetwork, extractedprism for kube-apiserver HA, OCI-tag-normalized cozy-installer chart, Platform Package, inline tenants/root ingress patch + LINSTOR pool registration during watch loop with combined HRs-Ready + pools-registered gate, Phase 8.6 default StorageClasses for v1.3.x, Phase 9.1 end-to-end reachability probe), debug (investigate a stuck or broken install — gathers symptoms, classifies operator error / config drift / upstream bug / not-yet-supported, applies fixes or workarounds, drafts upstream issues on approval; never opens PRs or files silently), cluster-upgrade (v1.x patch/minor upgrade with release-notes analysis), package-deploy (dev-loop deploy of a single package with ExternalArtifact support), package-bump (single-package version bump with changelog adaptation), external-app-create (scaffold a new external-apps package). All skills match the operator's natural language detected from conversation context — code identifiers, commands, file paths, and GitHub-public text stay canonical. All skills follow the same gate-and-confirm discipline: read-only lookups run freely; any mutation needs explicit per-step approval.", + "version": "1.15.0", + "description": "Cozystack platform skills bundle. Start with cozystack:wizard — it begins with a free-form 'tell me about your setup and goal' question, parses hints, then asks Talos / Ubuntu / Existing, builds a chain, and dispatches downstream skills via a cluster config directory the operator picks (every artifact lives there: inventory.yml, kubeconfig, .state.yaml, cozystack-platform-package.yaml — operator manages git on their own; optional sops opt-in encrypts secret files in-tree). Skills, invoked as cozystack:: wizard (orchestrator + 3-route dispatcher + Phase 4.5 active research + auto-dispatches debug on any failed_at), talos-bootstrap (Talos node prep via talm — Talos-1.12-aware maintenance probe, NAT-provider cert-SAN guardrail before first talm apply, multidoc machine-config with per-node VIP-link IPv4 stubs, etcd bootstrap, kubeconfig fetch, cozystack-tuned shape verification with Phase 11.5 auto-upgrade), talos-reset (cloud-provider terminate+relaunch helper for OCI/AWS/GCP/Hetzner when nodes are unrecoverable from inside; preserves block volumes + secondary VNICs + NSG memberships), ubuntu-bootstrap (wraps cozystack/ansible-cozystack — OS prep + k3s install in one go), cluster-install (Cozystack on a ready cluster — node-readiness, ZFS pool provisioning via privileged DaemonSet on Talos with hostNetwork, extractedprism for kube-apiserver HA, OCI-tag-normalized cozy-installer chart, Platform Package, inline tenants/root ingress patch + LINSTOR pool registration during watch loop with combined HRs-Ready + pools-registered gate, Phase 8.6 default StorageClasses for v1.3.x, Phase 9.1 end-to-end reachability probe), debug (investigate a stuck or broken install — gathers symptoms, classifies operator error / config drift / upstream bug / not-yet-supported, applies fixes or workarounds, drafts upstream issues on approval; never opens PRs or files silently), cluster-upgrade (v1.x patch/minor upgrade with release-notes analysis), package-deploy (dev-loop deploy of a single package with ExternalArtifact support), package-bump (single-package version bump with changelog adaptation), external-app-create (scaffold a new external-apps package), dev-ui-bootstrap (bootstrap a UI dev sandbox — Playwright + Vite dev server for cozystack-ui pointed at a chosen kubeconfig; worktree, @playwright/test, Chromium, playwright.config.ts, dev:e2e / test:e2e scripts, kubectl proxy + Vite on a free port). All skills match the operator's natural language detected from conversation context — code identifiers, commands, file paths, and GitHub-public text stay canonical. All skills follow the same gate-and-confirm discipline: read-only lookups run freely; any mutation needs explicit per-step approval.", "author": { "name": "Cozystack", "url": "https://github.com/cozystack" diff --git a/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md b/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md new file mode 100644 index 0000000..9b1140c --- /dev/null +++ b/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md @@ -0,0 +1,170 @@ +--- +name: dev-ui-bootstrap +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 ". 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. +argument-hint: "[--kubeconfig=] [--worktree=] [--port=] [--proxy-port=] [--no-install] [--no-browser]" +--- + +# cozystack:dev-ui-bootstrap + +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. + +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. + +Use the phrasing "`cozystack:dev-ui-bootstrap`" (not "the skill") in messages to the operator, and state progress at each phase boundary. + +Match the operator's natural language detected from prior conversation messages — use it in prompts, AskUserQuestion options, summaries, and gates. Code identifiers, commands, file paths, and commit messages stay in their canonical form (usually English). + +## Phase 1 — Parse arguments + +`$ARGUMENTS` is the free-form tail after `/cozystack:dev-ui-bootstrap`. Extract: + +- `--kubeconfig=` — 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. +- `--worktree=` — name (relative to `/.claude/worktrees/`) for the throwaway worktree. Default: `fix-app` (matches the in-tree convention). +- `--port=` — 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). +- `--proxy-port=` — `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). +- `--no-install` — skip `pnpm install` and `@playwright/test` install if they are already present. +- `--no-browser` — skip `npx playwright install chromium`. + +## Phase 2 — Locate the cozystack-ui repo + +Resolve in priority order: + +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`. +2. `~/aenix/cozystack-ui` — the canonical clone on contributors' workstations. +3. Otherwise — `AskUserQuestion` for the path. Do not `git clone`; the skill is for an already-cloned working copy. + +Verify `pnpm` is on `$PATH` and the workspace's `package.json` has `"packageManager": "pnpm@..."`. If `pnpm` is missing, stop and tell the operator to install it — do not auto-install package managers. + +## Phase 3 — Worktree creation + +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. + +```bash +git -C worktree add /.claude/worktrees/ -b fix/ main +``` + +If a worktree at that path already exists: + +- If clean and on the expected branch → reuse it; no `git worktree add`. +- If dirty or on a different branch → `AskUserQuestion`: reuse / pick a new name / cancel. + +Set `$WT` to the absolute worktree path. Every subsequent command in this skill runs from `$WT`. + +## Phase 4 — Install dependencies + +Always run `pnpm install` once in `$WT` (fast no-op if the lockfile is satisfied) — workspace symlinks must exist before Playwright lands. + +Then, unless `--no-install`: + +```bash +pnpm --filter @cozystack/console add -D @playwright/test +``` + +Unless `--no-browser`: + +```bash +cd $WT/apps/console && npx playwright install chromium +``` + +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. + +## Phase 5 — Write Playwright config + scripts + +Idempotency: if `apps/console/playwright.config.ts` already exists, diff against the template below and ask before overwriting — the operator may have customised it. + +Write `$WT/apps/console/playwright.config.ts`: + +```ts +import { defineConfig, devices } from "@playwright/test" + +export default defineConfig({ + testDir: "./e2e", + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: "list", + use: { + baseURL: process.env.E2E_BASE_URL ?? "http://localhost:", + trace: "on-first-retry", + screenshot: "only-on-failure", + }, + projects: [ + { name: "chromium", use: { ...devices["Desktop Chrome"] } }, + ], +}) +``` + +Replace `` 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. + +Create `$WT/apps/console/e2e/` (empty — tests land here as the operator writes them). + +Patch `$WT/apps/console/package.json` `"scripts"` with `Edit` — preserve unrelated keys, do not rewrite the whole file: + +- Add `"dev:e2e": "vite --port "` next to `"dev"`. +- Add `"test:e2e": "playwright test"` next to `"test"`. + +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. + +## Phase 6 — Bring up `kubectl proxy` + +Goal: a `kubectl proxy` instance is reachable on `$PROXY_PORT` and is talking to **the kubeconfig the operator picked**, not whatever happened to be active in the shell. + +1. Check whether `lsof -nP -iTCP:$PROXY_PORT -sTCP:LISTEN` (or `ss -tlnp`) already has a listener. +2. If yes, inspect the existing process command line (`ps -p -o args=`). If it is a `kubectl proxy` pointing at the same kubeconfig (look for `--kubeconfig` in the args; if absent, check `KUBECONFIG` env in `/proc//environ`), reuse it. Otherwise pick the next free port (start at `$PROXY_PORT+1`) and rewrite Vite's proxy `target` accordingly (see Phase 7). +3. If no listener, resolve the target context and start a new proxy in the background: + ```bash + CTX=$(kubectl --kubeconfig config current-context) + kubectl --kubeconfig --context $CTX proxy --port=$PROXY_PORT & + ``` +4. Smoke-test with `curl -sS -o /dev/null -w "%{http_code}" http://localhost:$PROXY_PORT/api/v1/namespaces/default`. Expect `200`. 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. + +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. + +## Phase 7 — Start the Vite dev server + +If `$PROXY_PORT` is non-default (8001), the dev server still hard-codes `http://localhost:8001` in `vite.config.ts`. Two options: + +- 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. +- If the operator explicitly said "change the proxy port", `AskUserQuestion` before touching `vite.config.ts`. + +Start the dev server in the background, capturing output: + +```bash +cd $WT && pnpm --filter @cozystack/console dev:e2e +``` + +Wait for the `ready in` / `Local:` line via a polling loop (`until grep -q ...`). Then `curl -sS -o /dev/null -w "%{http_code}" http://localhost:$PORT/` and expect `200`. + +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. + +## Phase 8 — Summary + +Print a compact handoff: + +- Worktree: `$WT` (branch `fix/`) +- Repo state: clean / dirty (one-line `git status -s`) +- Kubeconfig: `` → cluster context `` (resolved in Phase 6) +- `kubectl proxy`: `pid ` on `http://localhost:$PROXY_PORT` (smoke `200`) +- Vite dev: `pid ` on `http://localhost:$PORT` (smoke `200`) +- Playwright: configured at `apps/console/playwright.config.ts`, run with `pnpm --filter @cozystack/console test:e2e` +- Next steps the operator can take, e.g.: "Tell me what to fix, or paste a Playwright test idea and I will draft it." + +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. + +## Guardrails + +- **Never** run `git checkout` in the primary `cozystack-ui` working tree. All branch work happens inside the worktree from Phase 3. +- **Never** edit files in the primary working tree of `cozystack-ui` — only inside `$WT`. +- **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. +- **Never** start a `kubectl proxy` with a kubeconfig the operator did not explicitly pick. If `--kubeconfig` is absent, ask. +- **Never** kill a process the operator did not authorise. If a port is occupied, show what is on it and stop. +- **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. +- 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. + +## References + +- `apps/console/vite.config.ts` — the existing dev server proxy contract (`/api`, `/apis`, `/k8s` → `http://localhost:8001`). +- `apps/console/package.json` — where the new `dev:e2e` and `test:e2e` scripts land. +- `CLAUDE.md` (repo root) — code style (no semicolons, `.ts` extensions in imports, no `any`, etc.) and the worktree mandate. +- `~/.kube/` — separate kubeconfigs per cluster (this host uses one-file-per-cluster, not a merged config). From 3fbe0747326f74baee5b57101da8fb3883800f31 Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Tue, 19 May 2026 10:05:53 +0500 Subject: [PATCH 2/6] fix(dev-ui-bootstrap): pre-flight kubectl alongside pnpm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address review feedback from gemini-code-assist on plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md:36 — fail-fast on kubectl missing in Phase 2 instead of crashing deep in Phase 6 when `kubectl proxy` is first invoked. Co-Authored-By: Claude Opus 4.7 Signed-off-by: Myasnikov Daniil --- plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md b/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md index 9b1140c..b371bcd 100644 --- a/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md +++ b/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md @@ -33,7 +33,7 @@ Resolve in priority order: 2. `~/aenix/cozystack-ui` — the canonical clone on contributors' workstations. 3. Otherwise — `AskUserQuestion` for the path. Do not `git clone`; the skill is for an already-cloned working copy. -Verify `pnpm` is on `$PATH` and the workspace's `package.json` has `"packageManager": "pnpm@..."`. If `pnpm` is missing, stop and tell the operator to install it — do not auto-install package managers. +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. ## Phase 3 — Worktree creation From dca4314fb301f4de4ef3c20c1e3b53cf5b398b6a Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Tue, 19 May 2026 10:06:06 +0500 Subject: [PATCH 3/6] fix(dev-ui-bootstrap): use pnpm exec for playwright install MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address review feedback from gemini-code-assist on plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md:66 — the workspace is pnpm-managed; `pnpm exec` runs the workspace-installed binary directly without falling back to npx's lookup path. Keeps tooling consistent. Co-Authored-By: Claude Opus 4.7 Signed-off-by: Myasnikov Daniil --- plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md b/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md index b371bcd..bb2e002 100644 --- a/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md +++ b/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md @@ -63,7 +63,7 @@ pnpm --filter @cozystack/console add -D @playwright/test Unless `--no-browser`: ```bash -cd $WT/apps/console && npx playwright install chromium +cd $WT/apps/console && pnpm exec playwright install chromium ``` 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. From ed2f61d1ed38992ac8615970728e16785781f61f Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Tue, 19 May 2026 10:06:27 +0500 Subject: [PATCH 4/6] fix(dev-ui-bootstrap): resolve Phase 6 / Phase 7 port contradiction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address review feedback from gemini-code-assist on plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md:114 — Phase 6 step 2 used to silently switch the proxy port and "rewrite Vite's proxy target accordingly", which contradicts Phase 7's preference for leaving vite.config.ts untouched. Phase 6 now stops and asks the operator when port 8001 is occupied by an unrelated process — kill / different port / cancel. A port switch becomes an explicit operator decision that goes through Phase 7's vite.config.ts edit gate. Co-Authored-By: Claude Opus 4.7 Signed-off-by: Myasnikov Daniil --- plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md b/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md index bb2e002..62104b2 100644 --- a/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md +++ b/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md @@ -111,7 +111,7 @@ No semicolons in the config file — the repo's CLAUDE.md is strict: every file Goal: a `kubectl proxy` instance is reachable on `$PROXY_PORT` and is talking to **the kubeconfig the operator picked**, not whatever happened to be active in the shell. 1. Check whether `lsof -nP -iTCP:$PROXY_PORT -sTCP:LISTEN` (or `ss -tlnp`) already has a listener. -2. If yes, inspect the existing process command line (`ps -p -o args=`). If it is a `kubectl proxy` pointing at the same kubeconfig (look for `--kubeconfig` in the args; if absent, check `KUBECONFIG` env in `/proc//environ`), reuse it. Otherwise pick the next free port (start at `$PROXY_PORT+1`) and rewrite Vite's proxy `target` accordingly (see Phase 7). +2. If yes, inspect the existing process command line (`ps -p -o args=`). If it is a `kubectl proxy` pointing at the same kubeconfig (look for `--kubeconfig` in the args; if absent, check `KUBECONFIG` env in `/proc//environ`), reuse it. If it is something else (different kubeconfig, unrelated process), stop and `AskUserQuestion`: kill the occupant, pick a different proxy port (and accept the Phase 7 `vite.config.ts` edit gate), or cancel. Do not silently switch ports — Phase 7 prefers leaving `vite.config.ts` untouched, so a port switch is an operator-driven decision. 3. If no listener, resolve the target context and start a new proxy in the background: ```bash CTX=$(kubectl --kubeconfig config current-context) From c5b8a39fa506a137ee0b6e8f22716a02be2f8234 Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Tue, 19 May 2026 10:06:45 +0500 Subject: [PATCH 5/6] fix(dev-ui-bootstrap): smoke-test /api (no RBAC) not /api/v1/namespaces/default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address review feedback from gemini-code-assist on plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md:120 — listing the default namespace can return 403 on locked-down kubeconfigs even when the proxy is up and the credentials are valid. /api is the unauthenticated API-group discovery endpoint and answers 200 from any reachable apiserver, so it is a more reliable proxy-reachability probe. Co-Authored-By: Claude Opus 4.7 Signed-off-by: Myasnikov Daniil --- plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md b/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md index 62104b2..2ad9cdd 100644 --- a/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md +++ b/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md @@ -117,7 +117,7 @@ Goal: a `kubectl proxy` instance is reachable on `$PROXY_PORT` and is talking to CTX=$(kubectl --kubeconfig config current-context) kubectl --kubeconfig --context $CTX proxy --port=$PROXY_PORT & ``` -4. Smoke-test with `curl -sS -o /dev/null -w "%{http_code}" http://localhost:$PROXY_PORT/api/v1/namespaces/default`. Expect `200`. 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. +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. 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. From 56cb00d44821eb850f4d140b1068199d916554ea Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Tue, 19 May 2026 10:07:11 +0500 Subject: [PATCH 6/6] fix(dev-ui-bootstrap): redirect Vite output to a log file the polling loop can read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address review feedback from gemini-code-assist on plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md:137 — the background dev-server command had no output redirection, so the `until grep -q ...` polling loop had nothing to read. The server now writes to `$WT/.cozystack-dev-ui-bootstrap-vite.log` and the polling loop greps that file (with a 30 s timeout guard so a wedged Vite cannot hang the skill forever). Co-Authored-By: Claude Opus 4.7 Signed-off-by: Myasnikov Daniil --- plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md b/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md index 2ad9cdd..6155a4c 100644 --- a/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md +++ b/plugins/cozystack/skills/dev-ui-bootstrap/SKILL.md @@ -128,13 +128,14 @@ If `$PROXY_PORT` is non-default (8001), the dev server still hard-codes `http:// - 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. - If the operator explicitly said "change the proxy port", `AskUserQuestion` before touching `vite.config.ts`. -Start the dev server in the background, capturing output: +Start the dev server in the background, redirecting output to a log file under `$WT` so the polling loop has something to grep: ```bash -cd $WT && pnpm --filter @cozystack/console dev:e2e +cd $WT && pnpm --filter @cozystack/console dev:e2e \ + >$WT/.cozystack-dev-ui-bootstrap-vite.log 2>&1 & ``` -Wait for the `ready in` / `Local:` line via a polling loop (`until grep -q ...`). Then `curl -sS -o /dev/null -w "%{http_code}" http://localhost:$PORT/` and expect `200`. +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`. 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.