diff --git a/skills/create-qwik-city-vite/SKILL.md b/skills/create-qwik-city-vite/SKILL.md new file mode 100644 index 00000000..7c4fb50e --- /dev/null +++ b/skills/create-qwik-city-vite/SKILL.md @@ -0,0 +1,33 @@ +--- +name: create-qwik-city-vite +description: Creates a new Qwik City project with TypeScript support and installs custom AI rules. +inputs: + - id: workspace_name + name: Workspace Name + type: string + description: The name of the folder for the new project +--- + +## When to Use This Skill + +Use this skill when the user wants to create a new Qwik City project using `create-qwik`, and wants the project configured with custom AI rules. + +## Instructions + +1. **Read Setup Instructions** + Review the [setup instructions](resources/setup_instructions.md) to understand how to initialize the project and install dependencies. + + *Action:* Read `resources/setup_instructions.md`. + +2. **Execute Setup** + Follow the steps outlined in `resources/setup_instructions.md` to: + - Create the Qwik City project (using the `workspace_name` input). + - Install dependencies. + - Create the `.agents/rules/qwik-city-vite.md` file using the content from `resources/ai_rules.md`. + - Ensure the `.agents/rules/` directory exists. + +3. **Final Verification** + Check that: + - `package.json` exists in the new project. + - `.agents/rules/qwik-city-vite.md` exists. + - `src/routes/index.tsx` exists. diff --git a/skills/create-qwik-city-vite/resources/ai_rules.md b/skills/create-qwik-city-vite/resources/ai_rules.md new file mode 100644 index 00000000..09c14e05 --- /dev/null +++ b/skills/create-qwik-city-vite/resources/ai_rules.md @@ -0,0 +1,58 @@ +# Gemini AI Rules for Qwik City Projects + +## 1. Persona & Expertise + +You are an expert front-end developer specializing in Qwik and the Qwik City meta-framework. You are highly proficient in TypeScript and building high-performance, resumable web applications. You understand: +- Qwik's component model (`component$`) and Resumability. +- Fine-grained reactivity with `useSignal()` and `useStore()`. +- Server-side data loading with `routeLoader$`. +- Server-side code execution with `server$`. +- File-system based routing. +- Secure handling of secrets and environment variables. + +## 2. Project Context + +This project is a Qwik City application created using `npm create qwik@latest`. It is intended to be used as a Firebase Studio (formerly Project IDX) template/workspace and is also runnable locally. + +Default assumptions: +- Standard Qwik City project structure (`src/routes`, `src/components`). +- TypeScript is enabled by default. +- Vite is the development server and build tool. + +## 3. Development Environment + +This project is configured to run in a pre-built developer environment provided by Firebase Studio, where the environment is typically defined in `dev.nix`. + +When providing instructions: +- Assume Node.js is available in the Firebase Studio environment. +- Locally, the user must have Node.js installed (version 20+ recommended). +- Running the app is done via `npm run dev` and the app is served on `http://localhost:5173`. + +## 4. Coding Standards & Best Practices + +### General +- Prefer TypeScript (strict typing, explicit return types for exported functions when helpful). +- Keep components small and focused. +- Avoid introducing new dependencies unless necessary. +- After suggesting new dependencies, instruct the user to run `npm install `. + +### Qwik & Qwik City Specific +- **Component Structure:** Define components with `component$()`. All code inside is potentially serializable. +- **State Management:** Use `useSignal()` for simple values (string, number, boolean) and `useStore()` for objects and arrays. +- **Data Fetching:** Use `routeLoader$` for server-side data fetching tied to a route. The data is available via a hook in the component. +- **Styling:** Use `useStylesScoped$` to define component-scoped CSS that is automatically lazy-loaded. +- **Routing:** Follow the file-based routing conventions in the `src/routes` directory. +- **Security & Secrets:** + - Never expose API keys or other secrets in client-visible code (i.e., inside `component$`). + - Use `routeLoader$` or `server$` functions to handle secrets. These functions run *only* on the server. + - Access secrets via `process.env` within these server-only functions. + +## 5. Interaction Guidelines + +- Provide clear, actionable steps. +- When generating code, provide complete file contents for components (`.tsx`) or routes (`routes/**/index.tsx`). +- If the request is ambiguous, ask for clarification about: + - Where state should be managed (`useSignal` vs `useStore`). + - Whether data needs to be fetched on the server (`routeLoader$`). + - Where an action should be performed (client event handler vs `server$` function). +- Keep instructions compatible with both Firebase Studio (Nix-based environment) and local setups. diff --git a/skills/create-qwik-city-vite/resources/setup_instructions.md b/skills/create-qwik-city-vite/resources/setup_instructions.md new file mode 100644 index 00000000..bd2252df --- /dev/null +++ b/skills/create-qwik-city-vite/resources/setup_instructions.md @@ -0,0 +1,68 @@ +# Qwik City Vite Workspace Setup Instructions + +Follow these steps to initialize the workspace. + +## 1. Install prerequisites (Node.js + npm) + +This skill requires: +- Node.js (recommended 20.x+) +- npm (bundled with Node) + +### 1.1 Verify +Run: +- `node -v` +- `npm -v` + +If both work, go to **Step 2**. + +### 1.2 Install automatically from official Node.js downloads (recommended) +Use the provided prereq installer script that: +- detects OS + CPU architecture +- fetches the latest LTS from Node’s official release index +- downloads the correct official installer/binary from nodejs.org +- installs it + +Run ONE of the following depending on your OS: + +#### Windows (PowerShell) +Run: +- `powershell -ExecutionPolicy Bypass -File "skills/qwik-city-vite/scripts/install_node_official.ps1"` + +Then restart terminal / Antigravity session and verify: +- `node -v` +- `npm -v` + +#### macOS / Linux (bash) +Run: +- `bash "skills/qwik-city-vite/scripts/install_node_official.sh"` + +Then restart shell and verify: +- `node -v` +- `npm -v` + +--- + +## 2. Create the project + +Set workspace name: +- `WS_NAME=""` + +Then scaffold the project using the Qwik City template: + +```bash +npm create qwik@latest empty "$WS_NAME" +``` + +## 3. Install dependencies +cd "$WS_NAME" +npm install + +## 4. Configure Agent Rules + +```bash +mkdir -p ".agents/rules" +cp "../skills/qwik-city-vite/resources/ai_rules.md" ".agents/rules/qwik-city-vite.md" +``` + +## 5. Run server +npm run dev diff --git a/skills/create-qwik-city-vite/scripts/install_node_official.ps1 b/skills/create-qwik-city-vite/scripts/install_node_official.ps1 new file mode 100644 index 00000000..c4b29228 --- /dev/null +++ b/skills/create-qwik-city-vite/scripts/install_node_official.ps1 @@ -0,0 +1,103 @@ +# Installs the latest Node.js LTS from official nodejs.org releases (user-local install). +# - Detects CPU architecture +# - Fetches latest LTS from Node dist index.json +# - Downloads the official ZIP +# - Extracts to %LOCALAPPDATA%\Programs\nodejs\ +# - Adds to user PATH (no admin required) +# - Prompts to restart terminal / Antigravity + +$ErrorActionPreference = "Stop" +Set-StrictMode -Version Latest + +function Get-LatestLtsVersion { + $index = Invoke-RestMethod -Uri "https://nodejs.org/dist/index.json" + $lts = $index | Where-Object { $_.lts -ne $false } | Select-Object -First 1 + if (-not $lts -or -not $lts.version) { + throw "Could not determine latest LTS from https://nodejs.org/dist/index.json" + } + return $lts.version # e.g., v20.11.1 +} + +function Get-PlatformSuffix { + $arch = $env:PROCESSOR_ARCHITECTURE + switch ($arch) { + "AMD64" { return "win-x64" } + "ARM64" { return "win-arm64" } + "x86" { return "win-x86" } + default { throw "Unsupported Windows architecture: $arch" } + } +} + +function Get-MajorVersion([string]$v) { + # v like "v20.11.1" + $v = $v.Trim() + if ($v.StartsWith("v")) { $v = $v.Substring(1) } + return [int]($v.Split(".")[0]) +} + +# If Node already installed and >= 20, do nothing. +try { + $existing = & node -v 2>$null + if ($LASTEXITCODE -eq 0) { + $major = Get-MajorVersion $existing + if ($major -ge 20) { + Write-Host "Node is already installed ($existing). No action needed." + Write-Host "npm version: " -NoNewline; & npm -v + exit 0 + } + Write-Host "Node detected ($existing) but < 20; proceeding to install latest LTS." + } +} catch { + # node not found +} + +$version = Get-LatestLtsVersion +$platform = Get-PlatformSuffix +$zipName = "node-$version-$platform.zip" +$zipUrl = "https://nodejs.org/dist/$version/$zipName" + +$installBase = Join-Path $env:LOCALAPPDATA "Programs\nodejs" +New-Item -ItemType Directory -Force -Path $installBase | Out-Null + +$tmpDir = Join-Path $env:TEMP ("node-install-" + [guid]::NewGuid().ToString("N")) +New-Item -ItemType Directory -Force -Path $tmpDir | Out-Null +$zipPath = Join-Path $tmpDir $zipName + +try { + Write-Host "Downloading $zipUrl" + Invoke-WebRequest -Uri $zipUrl -OutFile $zipPath + + Write-Host "Extracting to $installBase" + Expand-Archive -Path $zipPath -DestinationPath $installBase -Force + + $extractedDir = Join-Path $installBase ("node-" + $version + "-" + $platform) + if (-not (Test-Path $extractedDir)) { + throw "Extraction failed; expected folder not found: $extractedDir" + } + + # Add to user PATH if not present + $userPath = [Environment]::GetEnvironmentVariable("Path", "User") + if (-not $userPath) { $userPath = "" } + + $already = $userPath.Split(";") | Where-Object { $_.Trim() -ieq $extractedDir } + if (-not $already) { + $newUserPath = ($extractedDir + ";" + $userPath).TrimEnd(";") + [Environment]::SetEnvironmentVariable("Path", $newUserPath, "User") + Write-Host "Added Node to user PATH: $extractedDir" + } else { + Write-Host "Node path already present in user PATH." + } + + # Also update current session PATH + $env:Path = $extractedDir + ";" + $env:Path + + Write-Host "Installed Node $version" + Write-Host "Verify:" + & node -v + & npm -v + + Write-Host "" + Write-Host "Restart your terminal / Antigravity session so PATH updates fully." +} finally { + if (Test-Path $tmpDir) { Remove-Item -Recurse -Force $tmpDir } +} diff --git a/skills/create-qwik-city-vite/scripts/install_node_official.sh b/skills/create-qwik-city-vite/scripts/install_node_official.sh new file mode 100644 index 00000000..6f663f38 --- /dev/null +++ b/skills/create-qwik-city-vite/scripts/install_node_official.sh @@ -0,0 +1,152 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Installs latest Node.js LTS from official nodejs.org releases (user-local install). +# - Detects OS + CPU arch +# - Fetches latest LTS from Node dist index.json +# - Downloads official tarball and installs under ~/.local/node/current +# - Updates shell profile PATH (bash/zsh/profile) +# - Prompts restart + +need_cmd() { command -v "$1" >/dev/null 2>&1; } + +if need_cmd node; then + ver="$(node -v)" + major="${ver#v}"; major="${major%%.*}" + if [[ "$major" -ge 20 ]]; then + echo "Node is already installed ($ver). No action needed." + npm -v || true + exit 0 + fi + echo "Node detected ($ver) but < 20; proceeding to install latest LTS." +fi + +DL_TOOL="" +if need_cmd curl; then DL_TOOL="curl -fsSL" +elif need_cmd wget; then DL_TOOL="wget -qO-" +else + echo "Error: need curl or wget" + exit 1 +fi + +OS="$(uname -s)" +ARCH="$(uname -m)" + +case "$OS" in + Darwin) OS_TAG="darwin" ;; + Linux) OS_TAG="linux" ;; + *) + echo "Unsupported OS: $OS" + exit 1 + ;; +esac + +case "$ARCH" in + x86_64|amd64) ARCH_TAG="x64" ;; + arm64|aarch64) ARCH_TAG="arm64" ;; + armv7l) ARCH_TAG="armv7l" ;; + *) + echo "Unsupported architecture: $ARCH" + exit 1 + ;; +esac + +INDEX_JSON="$($DL_TOOL https://nodejs.org/dist/index.json)" + +# Parse latest LTS version: +if need_cmd jq; then + VERSION="$(printf '%s' "$INDEX_JSON" | jq -r '.[] | select(.lts != false) | .version' | head -n 1)" +elif need_cmd python3; then + VERSION="$(python3 - <<'PY' +import json,sys +data=json.load(sys.stdin) +for r in data: + if r.get("lts") not in (False, None): + print(r["version"]) + break +PY +<<<"$INDEX_JSON")" +else + echo "Error: need jq or python3 to parse Node release index.json" + exit 1 +fi + +if [[ -z "${VERSION:-}" || "${VERSION}" == "null" ]]; then + echo "Error: could not determine latest LTS version" + exit 1 +fi + +PLATFORM="${OS_TAG}-${ARCH_TAG}" + +# Node uses .tar.gz for macOS builds, and .tar.xz for Linux builds. +EXT="tar.xz" +[[ "$OS_TAG" == "darwin" ]] && EXT="tar.gz" + +TARBALL="node-${VERSION}-${PLATFORM}.${EXT}" +URL="https://nodejs.org/dist/${VERSION}/${TARBALL}" + +INSTALL_BASE="${INSTALL_BASE:-$HOME/.local/node}" +INSTALL_DIR="${INSTALL_BASE}/${VERSION}" + +mkdir -p "$INSTALL_BASE" + +TMP="$(mktemp -d)" +cleanup() { rm -rf "$TMP"; } +trap cleanup EXIT + +echo "Downloading $URL" +if need_cmd curl; then + curl -fsSL "$URL" -o "$TMP/$TARBALL" +else + wget -q "$URL" -O "$TMP/$TARBALL" +fi + +echo "Installing to $INSTALL_DIR" +rm -rf "$INSTALL_DIR" +mkdir -p "$INSTALL_DIR" + +if [[ "$EXT" == "tar.gz" ]]; then + tar -xzf "$TMP/$TARBALL" -C "$TMP" +else + tar -xJf "$TMP/$TARBALL" -C "$TMP" +fi + +EXTRACTED="$TMP/node-${VERSION}-${PLATFORM}" +if [[ ! -d "$EXTRACTED" ]]; then + echo "Error: expected extracted folder not found: $EXTRACTED" + exit 1 +fi + +# Move contents into versioned install dir +cp -R "$EXTRACTED"/. "$INSTALL_DIR"/ + +# Update 'current' symlink +ln -sfn "$INSTALL_DIR" "$INSTALL_BASE/current" + +NODE_BIN="$INSTALL_BASE/current/bin" +EXPORT_LINE="export PATH=\"$NODE_BIN:\$PATH\"" +MARKER="# >>> antigravity node >>>" + +write_profile() { + local f="$1" + [[ -f "$f" ]] || touch "$f" + if ! grep -q "$MARKER" "$f"; then + { + echo "" + echo "$MARKER" + echo "$EXPORT_LINE" + echo "# <<< antigravity node <<<" + } >> "$f" + fi +} + +# Update common profiles +write_profile "$HOME/.bashrc" +write_profile "$HOME/.zshrc" +write_profile "$HOME/.profile" + +echo "" +echo "Installed Node ${VERSION} under $INSTALL_BASE/current" +echo "Restart your shell / Antigravity session, then verify:" +echo " node -v" +echo " npm -v"