diff --git a/skills/create-lit-vite/SKILL.md b/skills/create-lit-vite/SKILL.md new file mode 100644 index 00000000..cb9df430 --- /dev/null +++ b/skills/create-lit-vite/SKILL.md @@ -0,0 +1,42 @@ +--- +name: create-lit-vite +description: Creates a new Lit project using Vite with optional 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 + - id: language + name: Language + type: enum + default: ts + options: + js: JavaScript + ts: TypeScript +--- + +## When to Use This Skill + +Use this skill when the user wants to create a new Lit project using Vite, with either JavaScript or TypeScript, 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. **Guide the User** + Walk the user through the steps outlined in `resources/setup_instructions.md` to: + - Ensure Node.js is installed. + - Create the Lit-Vite project (using the `workspace_name` and `language` inputs). + - Install dependencies. + - Create the `.agents/rules/lit.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/lit.md` exists + - `index.html` exists + - `src/my-element.ts` (or `src/my-element.js` if using javascript) exists diff --git a/skills/create-lit-vite/resources/ai_rules.md b/skills/create-lit-vite/resources/ai_rules.md new file mode 100644 index 00000000..87f41080 --- /dev/null +++ b/skills/create-lit-vite/resources/ai_rules.md @@ -0,0 +1,53 @@ +# Gemini AI Rules for Lit Projects + +## 1. Persona & Expertise + +You are an expert front-end web developer specializing in Lit, Vite, and modern TypeScript. You understand: +- LitElement lifecycle +- Decorators (`@customElement`, `@property`, `@state`) +- Templating with `html` and `css` +- Event handling +- Performance and bundle optimization + +## 2. Project Context + +This project is a Lit application created using `npm create vite@latest -- --template lit`. It is intended to be used as a Firebase Studio (formerly Project IDX) template/workspace and also runnable locally if the user has the required tooling installed. + +Default assumptions: +- TypeScript is the primary language +- Vite is the 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 usually 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 ` (or the project’s package manager). + +### Lit Specific +- **Component Structure** + - Define custom elements with the `@customElement` decorator. + - Use `@property` for public properties and `@state` for internal state. +- **Templating** + - Use the `html` tagged template literal for rendering. + - Use the `css` tagged template literal for styling. +- **Performance** + - Use efficient event listeners. + - Be mindful of property updates and re-renders. + +## 5. Interaction Guidelines + +- Provide clear, actionable steps. +- When generating code, provide complete file contents. +- If the request is ambiguous, ask for clarification. +- Keep instructions compatible with both Firebase Studio (Nix-based environment) and local setups. \ No newline at end of file diff --git a/skills/create-lit-vite/resources/setup_instructions.md b/skills/create-lit-vite/resources/setup_instructions.md new file mode 100644 index 00000000..13591b56 --- /dev/null +++ b/skills/create-lit-vite/resources/setup_instructions.md @@ -0,0 +1,73 @@ +# Lit 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 "scripts/install_node_official.ps1"` + +Then restart terminal / Antigravity session and verify: +- `node -v` +- `npm -v` + +#### macOS / Linux (bash) +Run: +- `bash "scripts/install_node_official.sh"` + +Then restart shell and verify: +- `node -v` +- `npm -v` + +> Note: macOS/Linux install may require `sudo` for system-wide installation. + +--- + +## 2. Create the project + +Set workspace name: +- `WS_NAME=""` + +Then scaffold Lit: + +```bash +# TypeScript +npm create -y vite@latest "$WS_NAME" -- --template lit-ts + +# JavaScript +npm create -y vite@latest "$WS_NAME" -- --template lit +``` + +## 3. Install dependencies +cd "$WS_NAME" +npm install + +## 4. Configure Agents Rules + +Create: .agents/rules/lit.md inside the new workspace directory + +Copy the content from: resources/ai_rules.md + +## 5. Run server +npm run dev diff --git a/skills/create-lit-vite/scripts/install_node_official.ps1 b/skills/create-lit-vite/scripts/install_node_official.ps1 new file mode 100644 index 00000000..81169cc3 --- /dev/null +++ b/skills/create-lit-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 } +} \ No newline at end of file diff --git a/skills/create-lit-vite/scripts/install_node_official.sh b/skills/create-lit-vite/scripts/install_node_official.sh new file mode 100644 index 00000000..575e124f --- /dev/null +++ b/skills/create-lit-vite/scripts/install_node_official.sh @@ -0,0 +1,155 @@ +#!/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" +elif [[ "$EXT" == "tar.xz" ]]; then + tar -xJf "$TMP/$TARBALL" -C "$TMP" +else + echo "Unsupported archive format: $EXT" + exit 1 +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" \ No newline at end of file