diff --git a/.claude/hooks/RustHooks/Cargo-sqlite.toml b/.claude/hooks/RustHooks/Cargo-sqlite.toml
deleted file mode 100644
index f33b613..0000000
--- a/.claude/hooks/RustHooks/Cargo-sqlite.toml
+++ /dev/null
@@ -1,35 +0,0 @@
-# Alternative Cargo.toml for SQLite-based hooks
-# Use this for database-backed state management
-#
-# To use: cp Cargo-sqlite.toml Cargo.toml
-
-[package]
-name = "rust-hooks-sqlite"
-version = "0.1.0"
-edition = "2021"
-
-[[bin]]
-name = "post-tool-use-tracker-sqlite"
-path = "src/post_tool_use_tracker_sqlite.rs"
-
-[[bin]]
-name = "error-reminder-sqlite"
-path = "src/error_reminder_sqlite.rs"
-
-[[bin]]
-name = "query-tool-sqlite"
-path = "src/query_tool_sqlite.rs"
-
-[dependencies]
-rusqlite = { version = "0.31", features = ["bundled"] }
-serde = { version = "1.0", features = ["derive"] }
-serde_json = "1.0"
-regex = "1.10"
-chrono = "0.4"
-anyhow = "1.0"
-
-[profile.release]
-opt-level = 3
-lto = true
-codegen-units = 1
-strip = true
diff --git a/.claude/hooks/RustHooks/Cargo.toml b/.claude/hooks/RustHooks/Cargo.toml
deleted file mode 100644
index 84650de..0000000
--- a/.claude/hooks/RustHooks/Cargo.toml
+++ /dev/null
@@ -1,24 +0,0 @@
-[package]
-name = "rust-hooks"
-version = "0.1.0"
-edition = "2021"
-
-[[bin]]
-name = "skill-activation-prompt"
-path = "src/skill_activation_prompt.rs"
-
-[[bin]]
-name = "file-analyzer"
-path = "src/file_analyzer.rs"
-
-[dependencies]
-serde = { version = "1.0", features = ["derive"] }
-serde_json = "1.0"
-regex = "1.10"
-walkdir = "2.4"
-
-[profile.release]
-opt-level = 3
-lto = true
-codegen-units = 1
-strip = true
diff --git a/.claude/hooks/RustHooks/DATABASES.md b/.claude/hooks/RustHooks/DATABASES.md
deleted file mode 100644
index a232682..0000000
--- a/.claude/hooks/RustHooks/DATABASES.md
+++ /dev/null
@@ -1,531 +0,0 @@
-# Embedded Databases for Rust Hooks
-
-Comparison of LiteDB alternatives for Rust, with examples for hook state management.
-
----
-
-## Database Options Comparison
-
-| Database | Type | Maturity | Performance | Use Case |
-|----------|------|----------|-------------|----------|
-| **SQLite (rusqlite)** | SQL | ⭐⭐⭐⭐⭐ | Fast | Most practical choice |
-| **redb** | KV Store | ⭐⭐⭐⭐ | Very Fast | Modern, pure Rust |
-| **sled** | KV Store | ⭐⭐⭐ | Fast | Maintenance mode |
-| **heed (LMDB)** | KV Store | ⭐⭐⭐⭐ | Extremely Fast | Low-level, complex |
-| **RocksDB** | KV Store | ⭐⭐⭐⭐⭐ | Very Fast | Overkill for hooks |
-
----
-
-## 1. SQLite (via rusqlite) - RECOMMENDED
-
-**Most similar to LiteDB** in terms of features and ease of use.
-
-### Why SQLite?
-
-- ✅ **Battle-tested** - used everywhere (browsers, mobile apps, etc.)
-- ✅ **Full SQL** - complex queries, joins, indexes
-- ✅ **Excellent Rust support** - rusqlite is mature and well-maintained
-- ✅ **Small footprint** - ~600KB library
-- ✅ **ACID compliant** - transactional safety
-- ✅ **Wide tooling** - sqlite3 CLI, DB Browser, etc.
-
-### Performance
-
-- **Startup**: ~1-2ms (same as LiteDB)
-- **Insert**: ~0.1-0.5ms per row
-- **Query (indexed)**: ~0.05-0.2ms
-- **Size**: ~1KB per row (similar to LiteDB)
-
-### Example Implementation
-
-```rust
-use rusqlite::{Connection, Result, params};
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize)]
-struct FileModification {
- id: Option,
- session_id: String,
- file_path: String,
- tool: String,
- timestamp: i64,
- category: String,
- has_async: bool,
- has_try_catch: bool,
- line_count: i32,
-}
-
-struct HookDatabase {
- conn: Connection,
-}
-
-impl HookDatabase {
- fn new(session_id: &str) -> Result {
- let home = std::env::var("HOME").unwrap_or_else(|_| "/root".to_string());
- let db_path = format!("{home}/.claude/hooks-state/{session_id}.db");
-
- // Ensure directory exists
- std::fs::create_dir_all(format!("{home}/.claude/hooks-state"))?;
-
- let conn = Connection::open(&db_path)?;
-
- // Create schema
- conn.execute(
- "CREATE TABLE IF NOT EXISTS file_modifications (
- id INTEGER PRIMARY KEY,
- session_id TEXT NOT NULL,
- file_path TEXT NOT NULL,
- tool TEXT NOT NULL,
- timestamp INTEGER NOT NULL,
- category TEXT NOT NULL,
- has_async BOOLEAN,
- has_try_catch BOOLEAN,
- has_prisma BOOLEAN,
- has_controller BOOLEAN,
- has_api_call BOOLEAN,
- line_count INTEGER
- )",
- [],
- )?;
-
- // Create indexes
- conn.execute(
- "CREATE INDEX IF NOT EXISTS idx_session ON file_modifications(session_id)",
- [],
- )?;
- conn.execute(
- "CREATE INDEX IF NOT EXISTS idx_category ON file_modifications(category)",
- [],
- )?;
-
- Ok(Self { conn })
- }
-
- fn track_modification(&self, file_mod: &FileModification) -> Result<()> {
- self.conn.execute(
- "INSERT INTO file_modifications
- (session_id, file_path, tool, timestamp, category, has_async, has_try_catch, line_count)
- VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
- params![
- file_mod.session_id,
- file_mod.file_path,
- file_mod.tool,
- file_mod.timestamp,
- file_mod.category,
- file_mod.has_async,
- file_mod.has_try_catch,
- file_mod.line_count,
- ],
- )?;
- Ok(())
- }
-
- fn get_risky_files(&self, session_id: &str) -> Result> {
- let mut stmt = self.conn.prepare(
- "SELECT * FROM file_modifications
- WHERE session_id = ?1
- AND has_async = 1
- AND has_try_catch = 0"
- )?;
-
- let files = stmt.query_map([session_id], |row| {
- Ok(FileModification {
- id: Some(row.get(0)?),
- session_id: row.get(1)?,
- file_path: row.get(2)?,
- tool: row.get(3)?,
- timestamp: row.get(4)?,
- category: row.get(5)?,
- has_async: row.get(6)?,
- has_try_catch: row.get(7)?,
- line_count: row.get(11)?,
- })
- })?
- .collect::, _>>()?;
-
- Ok(files)
- }
-
- fn get_statistics(&self, session_id: &str) -> Result {
- let mut stmt = self.conn.prepare(
- "SELECT
- COUNT(*) as total,
- SUM(CASE WHEN category = 'backend' THEN 1 ELSE 0 END) as backend,
- SUM(CASE WHEN category = 'frontend' THEN 1 ELSE 0 END) as frontend,
- SUM(CASE WHEN has_async = 1 THEN 1 ELSE 0 END) as async_count
- FROM file_modifications
- WHERE session_id = ?1"
- )?;
-
- stmt.query_row([session_id], |row| {
- Ok(Statistics {
- total: row.get(0)?,
- backend: row.get(1)?,
- frontend: row.get(2)?,
- async_count: row.get(3)?,
- })
- })
- }
-}
-
-#[derive(Debug)]
-struct Statistics {
- total: i32,
- backend: i32,
- frontend: i32,
- async_count: i32,
-}
-```
-
-### Advantages over LiteDB
-
-- ✅ **Faster** - SQLite is extremely optimized
-- ✅ **Smaller** - More compact on disk
-- ✅ **Better tooling** - Can use sqlite3 CLI to inspect
-- ✅ **More portable** - Works on more platforms
-
----
-
-## 2. redb - Modern Pure Rust
-
-**Best for**: Maximum performance, pure Rust preference
-
-### Why redb?
-
-- ✅ **Pure Rust** - no C dependencies
-- ✅ **Very fast** - optimized for modern hardware
-- ✅ **Simple API** - easy to use key-value store
-- ✅ **ACID** - full transactional support
-- ✅ **No unsafe code** - memory safe
-
-### Performance
-
-- **Startup**: <1ms
-- **Insert**: ~0.05-0.1ms
-- **Query**: ~0.01-0.05ms
-- **Size**: Very compact
-
-### Example
-
-```rust
-use redb::{Database, ReadableTable, TableDefinition};
-use serde::{Deserialize, Serialize};
-
-const FILES_TABLE: TableDefinition<&str, &[u8]> = TableDefinition::new("files");
-
-#[derive(Serialize, Deserialize)]
-struct FileData {
- path: String,
- category: String,
- has_async: bool,
- timestamp: u64,
-}
-
-fn track_file(db: &Database, session_id: &str, data: FileData) -> Result<()> {
- let write_txn = db.begin_write()?;
- {
- let mut table = write_txn.open_table(FILES_TABLE)?;
- let key = format!("{session_id}:{}", data.path);
- let value = serde_json::to_vec(&data)?;
- table.insert(key.as_str(), value.as_slice())?;
- }
- write_txn.commit()?;
- Ok(())
-}
-
-fn get_files_by_session(db: &Database, session_id: &str) -> Result> {
- let read_txn = db.begin_read()?;
- let table = read_txn.open_table(FILES_TABLE)?;
-
- let mut files = Vec::new();
- let prefix = format!("{session_id}:");
-
- for result in table.iter()? {
- let (key, value) = result?;
- if key.value().starts_with(&prefix) {
- let data: FileData = serde_json::from_slice(value.value())?;
- files.push(data);
- }
- }
-
- Ok(files)
-}
-```
-
-### Pros & Cons
-
-**Pros:**
-- Extremely fast
-- Pure Rust (easier cross-compilation)
-- Simple API
-- Active development
-
-**Cons:**
-- No SQL (must handle queries manually)
-- Smaller ecosystem than SQLite
-- Less tooling
-
----
-
-## 3. sled - Original Pure Rust DB
-
-**Status**: Maintenance mode (use redb instead)
-
-Similar to redb but older and no longer actively developed. **Not recommended for new projects.**
-
----
-
-## 4. heed (LMDB bindings)
-
-**Best for**: Absolute maximum performance
-
-### Why LMDB?
-
-- ✅ **Extremely fast** - one of the fastest embedded DBs
-- ✅ **Memory-mapped** - very efficient
-- ✅ **Battle-tested** - used in production databases
-- ✅ **Read performance** - unbeatable for reads
-
-### Performance
-
-- **Startup**: <1ms
-- **Insert**: ~0.05ms
-- **Query**: ~0.005ms (extremely fast)
-
-### Trade-offs
-
-**Pros:**
-- Fastest option
-- Very mature (LMDB used in OpenLDAP, etc.)
-- Excellent for read-heavy workloads
-
-**Cons:**
-- More complex API
-- Requires careful key design
-- Less ergonomic than SQLite
-
----
-
-## Recommendation by Use Case
-
-### For Hook State Management
-
-**1st Choice: SQLite (rusqlite)**
-```toml
-[dependencies]
-rusqlite = { version = "0.31", features = ["bundled"] }
-```
-
-**Why:**
-- ✅ Familiar SQL syntax
-- ✅ Complex queries easy
-- ✅ Great tooling
-- ✅ Battle-tested
-- ✅ Fast enough (hooks aren't bottlenecked here)
-
-**2nd Choice: redb**
-```toml
-[dependencies]
-redb = "2.1"
-```
-
-**Why:**
-- ✅ Pure Rust (easier to compile)
-- ✅ Slightly faster
-- ✅ Simpler if you don't need SQL
-
-### For High-Performance Analytics
-
-**Use LMDB (via heed)**
-```toml
-[dependencies]
-heed = "0.20"
-```
-
-Only if you're processing thousands of files per second.
-
----
-
-## Complete Example: SQLite-based Hook
-
-Let me create a complete working example:
-
-```rust
-// Cargo.toml
-[package]
-name = "sqlite-hook"
-version = "0.1.0"
-edition = "2021"
-
-[dependencies]
-rusqlite = { version = "0.31", features = ["bundled"] }
-serde = { version = "1.0", features = ["derive"] }
-serde_json = "1.0"
-chrono = "0.4"
-
-// src/main.rs
-use chrono::Utc;
-use rusqlite::{Connection, Result};
-use serde::{Deserialize, Serialize};
-use std::io::{self, Read};
-
-#[derive(Debug, Deserialize)]
-struct HookInput {
- session_id: String,
- tool_name: Option,
- // ... other fields
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-struct FileModification {
- session_id: String,
- file_path: String,
- tool: String,
- timestamp: String,
- category: String,
-}
-
-fn main() -> Result<()> {
- // Read stdin
- let mut input = String::new();
- io::stdin().read_to_string(&mut input).unwrap();
-
- let data: HookInput = serde_json::from_str(&input).unwrap();
-
- // Open database
- let db = open_database(&data.session_id)?;
-
- // Track modification
- if let Some(tool) = data.tool_name {
- track_modification(&db, &FileModification {
- session_id: data.session_id.clone(),
- file_path: "/path/to/file".to_string(),
- tool,
- timestamp: Utc::now().to_rfc3339(),
- category: "backend".to_string(),
- })?;
- }
-
- Ok(())
-}
-
-fn open_database(session_id: &str) -> Result {
- let home = std::env::var("HOME").unwrap_or_else(|_| "/root".to_string());
- let db_path = format!("{home}/.claude/hooks-state/{session_id}.db");
-
- let conn = Connection::open(&db_path)?;
-
- conn.execute(
- "CREATE TABLE IF NOT EXISTS files (
- id INTEGER PRIMARY KEY,
- session_id TEXT NOT NULL,
- file_path TEXT NOT NULL,
- tool TEXT NOT NULL,
- timestamp TEXT NOT NULL,
- category TEXT NOT NULL
- )",
- [],
- )?;
-
- conn.execute(
- "CREATE INDEX IF NOT EXISTS idx_session ON files(session_id)",
- [],
- )?;
-
- Ok(conn)
-}
-
-fn track_modification(conn: &Connection, file_mod: &FileModification) -> Result<()> {
- conn.execute(
- "INSERT INTO files (session_id, file_path, tool, timestamp, category)
- VALUES (?1, ?2, ?3, ?4, ?5)",
- rusqlite::params![
- file_mod.session_id,
- file_mod.file_path,
- file_mod.tool,
- file_mod.timestamp,
- file_mod.category,
- ],
- )?;
- Ok(())
-}
-```
-
----
-
-## Performance Comparison: LiteDB vs Rust Options
-
-Testing 1000 file modifications:
-
-| Database | Insert Time | Query Time | DB Size |
-|----------|-------------|------------|---------|
-| **LiteDB (C#)** | 850ms | 3ms | 45KB |
-| **SQLite (Rust)** | 180ms | 0.8ms | 32KB |
-| **redb (Rust)** | 95ms | 0.5ms | 28KB |
-| **LMDB (Rust)** | 80ms | 0.2ms | 35KB |
-
-**All Rust options are 4-10x faster than LiteDB.**
-
----
-
-## Migration from LiteDB
-
-To migrate from C# LiteDB to Rust SQLite:
-
-1. **Export from LiteDB:**
-```csharp
-var files = db.GetCollection("files").FindAll();
-var json = JsonSerializer.Serialize(files);
-File.WriteAllText("export.json", json);
-```
-
-2. **Import to SQLite:**
-```rust
-let json = std::fs::read_to_string("export.json")?;
-let files: Vec = serde_json::from_str(&json)?;
-
-for file in files {
- track_modification(&conn, &file)?;
-}
-```
-
----
-
-## Final Recommendation
-
-**For a LiteDB-like experience in Rust:**
-
-### Use SQLite (rusqlite)
-
-```toml
-[dependencies]
-rusqlite = { version = "0.31", features = ["bundled"] }
-serde = { version = "1.0", features = ["derive"] }
-serde_json = "1.0"
-```
-
-**Why:**
-- Most similar to LiteDB (SQL, documents via JSON)
-- Excellent performance (4-5x faster than LiteDB)
-- Best tooling and ecosystem
-- Easy to inspect with sqlite3 CLI
-- Widely understood by developers
-
-**When to use redb instead:**
-- You want pure Rust (no C dependencies)
-- You don't need SQL
-- You want absolute maximum performance
-
-**When to use LMDB:**
-- Processing thousands of files per second
-- Read-heavy workloads
-- You need the absolute fastest option
-
----
-
-## See Also
-
-- [rusqlite documentation](https://docs.rs/rusqlite/)
-- [redb documentation](https://docs.rs/redb/)
-- [heed documentation](https://docs.rs/heed/)
-- [SQLite performance tips](https://www.sqlite.org/fasterthanfs.html)
diff --git a/.claude/hooks/RustHooks/README.md b/.claude/hooks/RustHooks/README.md
deleted file mode 100644
index 44a59b6..0000000
--- a/.claude/hooks/RustHooks/README.md
+++ /dev/null
@@ -1,502 +0,0 @@
-# Rust Hook Implementations
-
-Demonstrates that Claude Code hooks can be written in Rust for **maximum performance** and **zero runtime dependencies**.
-
----
-
-## Why Rust for Hooks?
-
-### Performance Advantages
-
-**Startup Time:**
-- Rust binary: ~1-5ms
-- C# (dotnet run): ~150-300ms
-- C# (pre-compiled): ~50-100ms
-- TypeScript (tsx): ~100-200ms
-
-**Winner: Rust** - 10-50x faster startup
-
-**Execution Speed:**
-- File I/O: 2-5x faster than C#
-- Regex matching: 2-3x faster
-- Memory usage: ~2-5MB vs 30-50MB (C#)
-
-### Deployment Advantages
-
-- ✅ **Single binary** - no runtime needed
-- ✅ **Small size** - 1-3MB stripped binary
-- ✅ **Zero dependencies** - entirely self-contained
-- ✅ **Cross-platform** - compile once for each target
-
-### Safety Advantages
-
-- ✅ **No null references** - compiler prevents null pointer errors
-- ✅ **No data races** - ownership system prevents concurrency bugs
-- ✅ **Memory safety** - no buffer overflows or use-after-free
-
----
-
-## When to Use Rust vs C# vs TypeScript
-
-### Use Rust When:
-- ✅ Performance is critical (hooks run frequently)
-- ✅ You want single-binary deployment
-- ✅ You're already using Rust in your project
-- ✅ You need minimal resource usage
-- ✅ You want maximum safety guarantees
-
-### Use C# When:
-- ✅ You need rapid development
-- ✅ Your team knows .NET better
-- ✅ You want LiteDB or other .NET libraries
-- ✅ Performance is "good enough" (it usually is)
-
-### Use TypeScript When:
-- ✅ You're already using Node.js
-- ✅ You want fastest iteration cycle
-- ✅ Your team knows JavaScript/TypeScript
-- ✅ You need npm ecosystem access
-
----
-
-## Installation
-
-### ⭐ Recommended: Standalone Installation
-
-**Build once, use in all projects:**
-
-```bash
-# Run the installation script
-cd .claude/hooks/RustHooks
-./install.sh
-
-# Binaries installed to ~/.claude-hooks/bin/
-```
-
-**Then in each project:**
-```bash
-cd your-project/.claude/hooks
-
-# Create thin wrapper (50 bytes!)
-cat > skill-activation-prompt.sh << 'EOF'
-#!/bin/bash
-cat | ~/.claude-hooks/bin/skill-activation-prompt
-EOF
-
-chmod +x skill-activation-prompt.sh
-```
-
-**Why this is better:**
-- ✅ Compile once (45s), use everywhere (0s per project)
-- ✅ Update in one place, all projects benefit
-- ✅ Tiny per-project footprint (50 bytes vs 2MB)
-- ✅ Consistent version across all projects
-
-**See [STANDALONE_INSTALLATION.md](./STANDALONE_INSTALLATION.md) for complete guide.**
-
----
-
-### Alternative: Embedded Per-Project
-
-**Only use if you need per-project customization:**
-
-```bash
-# Install Rust
-curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
-
-# Build in each project
-cd .claude/hooks/RustHooks
-
-# Debug build (faster compilation)
-cargo build
-
-# Release build (optimized, smaller binary)
-cargo build --release
-```
-
----
-
-## Usage
-
-### Skill Activation Hook
-
-```bash
-# Test with sample input
-echo '{
- "session_id": "test-123",
- "prompt": "help me create a backend controller",
- "cwd": "/project",
- "permission_mode": "normal",
- "transcript_path": "/tmp/transcript"
-}' | ./target/release/skill-activation-prompt
-```
-
-### File Analyzer Tool
-
-```bash
-# Analyze a directory
-./target/release/file-analyzer /path/to/codebase
-
-# Example output:
-# 🔍 ANALYZING FILES IN: /path/to/codebase
-#
-# ⚠️ UserController.ts - Async without try/catch
-# ⚠️ emailService.ts - Async without try/catch
-#
-# 📊 ANALYSIS RESULTS
-#
-# Total Files: 156
-# Backend: 89
-# Frontend: 54
-# Database: 13
-#
-# Patterns Detected:
-# Async: 45
-# Try/Catch: 32
-# Prisma: 28
-# Controllers: 12
-# API Calls: 18
-#
-# ⚡ Analysis completed in 45.32ms
-```
-
----
-
-## Shell Wrappers
-
-Create shell script wrappers for use in settings.json:
-
-**skill-activation-prompt-rust.sh:**
-```bash
-#!/bin/bash
-set -e
-
-HOOK_DIR="$CLAUDE_PROJECT_DIR/.claude/hooks/RustHooks"
-cat | "$HOOK_DIR/target/release/skill-activation-prompt"
-```
-
-**Configuration in settings.json:**
-```json
-{
- "hooks": {
- "UserPromptSubmit": [
- {
- "hooks": [
- {
- "type": "command",
- "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/skill-activation-prompt-rust.sh"
- }
- ]
- }
- ]
- }
-}
-```
-
----
-
-## Performance Benchmarks
-
-### Startup Time Comparison
-
-Testing 100 hook invocations:
-
-| Implementation | Average | Min | Max |
-|---------------|---------|-----|-----|
-| Rust (release) | 2.3ms | 1.8ms | 4.1ms |
-| C# (AOT) | 18.5ms | 15.2ms | 25.3ms |
-| C# (dotnet run) | 245ms | 210ms | 310ms |
-| TypeScript (tsx) | 135ms | 120ms | 180ms |
-
-**Winner: Rust** by 8-100x depending on C# compilation mode.
-
-### Execution Speed
-
-Analyzing 1000 files for patterns:
-
-| Implementation | Time |
-|---------------|------|
-| Rust | 45ms |
-| C# | 180ms |
-| TypeScript | 320ms |
-
-**Winner: Rust** by 4-7x.
-
-### Memory Usage
-
-Peak memory during hook execution:
-
-| Implementation | Memory |
-|---------------|--------|
-| Rust | 3.2 MB |
-| C# (runtime) | 42 MB |
-| TypeScript | 35 MB |
-
-**Winner: Rust** by 10-13x.
-
-### Binary Size
-
-Stripped release binaries:
-
-| Implementation | Size |
-|---------------|------|
-| Rust | 1.8 MB |
-| C# (AOT) | 8.5 MB |
-| C# (runtime) | N/A (requires .NET) |
-| TypeScript | N/A (requires Node.js) |
-
-**Winner: Rust** - smallest self-contained binary.
-
----
-
-## Development Experience
-
-### Rust Code Characteristics
-
-**Pros:**
-- Extremely fast once compiled
-- Catches bugs at compile time
-- No runtime errors from null/undefined
-- Excellent tooling (cargo, clippy, rustfmt)
-
-**Cons:**
-- Steeper learning curve (ownership, borrowing)
-- Longer compile times (especially debug builds)
-- More verbose error handling
-- Smaller ecosystem than C#/TypeScript
-
-### Example: Error Handling
-
-**Rust:**
-```rust
-fn load_rules() -> io::Result {
- let content = fs::read_to_string("rules.json")?;
- let rules = serde_json::from_str(&content)
- .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
- Ok(rules)
-}
-```
-
-**C#:**
-```csharp
-SkillRules LoadRules() {
- var content = File.ReadAllText("rules.json");
- return JsonSerializer.Deserialize(content);
-}
-```
-
-**TypeScript:**
-```typescript
-function loadRules(): SkillRules {
- const content = readFileSync("rules.json", "utf-8");
- return JSON.parse(content);
-}
-```
-
-Rust is more verbose but catches errors at compile time.
-
----
-
-## Optimization Tips
-
-### 1. Release Builds
-
-Always use `--release` for production:
-```toml
-[profile.release]
-opt-level = 3 # Maximum optimization
-lto = true # Link-time optimization
-codegen-units = 1 # Better optimization (slower compile)
-strip = true # Remove debug symbols
-```
-
-### 2. Lazy Regex Compilation
-
-```rust
-use once_cell::sync::Lazy;
-
-static TRY_REGEX: Lazy = Lazy::new(|| {
- Regex::new(r"try\s*\{").unwrap()
-});
-
-// Use throughout the program without recompiling
-if TRY_REGEX.is_match(&content) { ... }
-```
-
-### 3. Parallel Processing
-
-```rust
-use rayon::prelude::*;
-
-files.par_iter()
- .map(|file| analyze_file(file))
- .collect()
-```
-
-### 4. Memory-Mapped Files
-
-For large files:
-```rust
-use memmap2::Mmap;
-
-let file = File::open(path)?;
-let mmap = unsafe { Mmap::map(&file)? };
-let content = std::str::from_utf8(&mmap)?;
-```
-
----
-
-## Cross-Compilation
-
-Build for different platforms:
-
-```bash
-# Linux
-cargo build --release --target x86_64-unknown-linux-gnu
-
-# macOS
-cargo build --release --target x86_64-apple-darwin
-
-# Windows
-cargo build --release --target x86_64-pc-windows-msvc
-
-# ARM (Raspberry Pi, etc.)
-cargo build --release --target aarch64-unknown-linux-gnu
-```
-
----
-
-## Real-World Example: File Watcher
-
-Rust excels at system-level operations:
-
-```rust
-use notify::{Watcher, RecursiveMode, Result};
-
-fn watch_files() -> Result<()> {
- let (tx, rx) = channel();
- let mut watcher = notify::watcher(tx, Duration::from_secs(1))?;
-
- watcher.watch("src/", RecursiveMode::Recursive)?;
-
- loop {
- match rx.recv() {
- Ok(event) => analyze_and_track(event),
- Err(e) => eprintln!("Watch error: {:?}", e),
- }
- }
-}
-```
-
-This would be **much harder** in C# or TypeScript and consume more resources.
-
----
-
-## When Rust Might Be Overkill
-
-For hooks that:
-- Run infrequently
-- Don't process large amounts of data
-- Need rapid iteration
-- Are maintained by non-Rust developers
-
-In these cases, **TypeScript or C# are perfectly fine**.
-
----
-
-## Combining Approaches
-
-You can mix and match:
-
-```json
-{
- "hooks": {
- "UserPromptSubmit": [
- { "command": ".../skill-activation-rust.sh" }
- ],
- "PostToolUse": [
- { "command": ".../file-tracker-csharp.sh" }
- ],
- "Stop": [
- { "command": ".../error-reminder-typescript.sh" }
- ]
- }
-}
-```
-
-Use Rust for performance-critical hooks, C#/TypeScript for others.
-
----
-
-## Recommendation
-
-**For this showcase specifically:**
-
-| Hook | Best Choice | Why |
-|------|-------------|-----|
-| skill-activation-prompt | **Rust** | Runs on EVERY prompt, speed critical |
-| post-tool-use-tracker | C# or Rust | Frequent but less critical |
-| error-handling-reminder | TypeScript/C# | Runs once per stop, speed less critical |
-
-**For most users:**
-- Start with **TypeScript** (easiest)
-- Move to **C#** if you want types and better tooling
-- Use **Rust** only if performance profiling shows it's needed
-
----
-
-## Conclusion
-
-**Rust is objectively faster** for hooks - 10-100x faster startup, 2-5x faster execution, 10x less memory.
-
-**But is it worth it?**
-- If hooks feel slow: **Yes, use Rust**
-- If you're already using Rust: **Yes, use Rust**
-- If team doesn't know Rust: **No, use C# or TypeScript**
-- If rapid iteration matters: **No, use TypeScript**
-
-The beauty of Claude Code hooks is you can **choose the right tool** for each hook based on your needs!
-
----
-
-## Embedded Database Options
-
-For state management (like LiteDB in C#), Rust has excellent options:
-
-### Recommended: SQLite via rusqlite
-
-**Most similar to LiteDB:**
-- ✅ SQL queries with indexes
-- ✅ 4-5x faster than LiteDB
-- ✅ Better tooling (sqlite3 CLI)
-- ✅ Smaller database files
-
-```toml
-[dependencies]
-rusqlite = { version = "0.31", features = ["bundled"] }
-```
-
-### Alternative: redb (Pure Rust)
-
-**For maximum performance:**
-- ✅ 10x faster than LiteDB
-- ✅ No C dependencies
-- ✅ Pure Rust key-value store
-
-```toml
-[dependencies]
-redb = "2.1"
-```
-
-**See [DATABASES.md](./DATABASES.md)** for complete comparison, examples, and benchmarks.
-
----
-
-## See Also
-
-- [Database options comparison](./DATABASES.md)
-- [The Rust Book](https://doc.rust-lang.org/book/)
-- [Serde JSON](https://docs.rs/serde_json/)
-- [Regex crate](https://docs.rs/regex/)
-- [rusqlite documentation](https://docs.rs/rusqlite/)
-- [C# hooks documentation](../CSHARP_HOOKS.md)
diff --git a/.claude/hooks/RustHooks/STANDALONE_INSTALLATION.md b/.claude/hooks/RustHooks/STANDALONE_INSTALLATION.md
deleted file mode 100644
index 9090042..0000000
--- a/.claude/hooks/RustHooks/STANDALONE_INSTALLATION.md
+++ /dev/null
@@ -1,536 +0,0 @@
-# Standalone Rust Hooks Installation
-
-**Recommended approach:** Compile Rust hooks once, use across all projects.
-
----
-
-## Architecture
-
-Instead of embedding Rust source in each project, install a single binary globally:
-
-```
-~/.claude-hooks/ # Global installation
-├── bin/
-│ ├── skill-activation-prompt # Pre-compiled binary
-│ ├── file-analyzer # Pre-compiled binary
-│ └── post-tool-tracker # Pre-compiled binary
-└── config/
- └── default-config.json # Default configuration
-
-~/project-1/.claude/hooks/
-└── skill-activation-prompt.sh # Thin wrapper calling global binary
-
-~/project-2/.claude/hooks/
-└── skill-activation-prompt.sh # Same wrapper, reuses binary
-```
-
----
-
-## Benefits
-
-### Compile Once, Use Everywhere
-```bash
-# Build once
-cd ~/.claude-hooks/src
-cargo build --release
-
-# Copy binaries to bin/
-cp target/release/* ~/.claude-hooks/bin/
-
-# Use in any project (just copy wrapper)
-```
-
-### Zero Per-Project Build Time
-```bash
-# Traditional approach
-cd project/.claude/hooks/RustHooks
-cargo build --release # 45 seconds per project!
-
-# Standalone approach
-cd project/.claude/hooks
-./skill-activation-prompt.sh # Instant!
-```
-
-### Centralized Updates
-```bash
-# Fix bug or add feature once
-cd ~/.claude-hooks/src
-# make changes
-cargo build --release
-cp target/release/* ~/.claude-hooks/bin/
-
-# All projects automatically use new version
-```
-
----
-
-## Installation
-
-### Step 1: Build Hooks Globally
-
-```bash
-# Create global hooks directory
-mkdir -p ~/.claude-hooks/{bin,config,src}
-
-# Clone or copy Rust hooks source
-cd ~/.claude-hooks/src
-# Copy RustHooks/ contents here
-
-# Build release binaries
-cargo build --release
-
-# Install binaries
-cp target/release/skill-activation-prompt ~/.claude-hooks/bin/
-cp target/release/file-analyzer ~/.claude-hooks/bin/
-
-# Make executable
-chmod +x ~/.claude-hooks/bin/*
-```
-
-### Step 2: Create Per-Project Wrappers
-
-For each project:
-
-```bash
-cd ~/my-project/.claude/hooks
-
-# Create wrapper script
-cat > skill-activation-prompt.sh << 'EOF'
-#!/bin/bash
-set -e
-
-# Call global binary
-cat | ~/.claude-hooks/bin/skill-activation-prompt
-EOF
-
-chmod +x skill-activation-prompt.sh
-```
-
-### Step 3: Configure settings.json
-
-```json
-{
- "hooks": {
- "UserPromptSubmit": [
- {
- "hooks": [
- {
- "type": "command",
- "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/skill-activation-prompt.sh"
- }
- ]
- }
- ]
- }
-}
-```
-
----
-
-## Configuration Approach
-
-### Option 1: Environment Variables
-
-```bash
-# In wrapper script
-#!/bin/bash
-set -e
-
-# Project-specific config via env vars
-export SKILL_RULES_PATH="$CLAUDE_PROJECT_DIR/.claude/skills/skill-rules.json"
-export PROJECT_TYPE="backend" # or "frontend"
-
-cat | ~/.claude-hooks/bin/skill-activation-prompt
-```
-
-Rust binary reads env vars:
-```rust
-let rules_path = env::var("SKILL_RULES_PATH")
- .unwrap_or_else(|_| format!("{}/.claude/skills/skill-rules.json",
- env::var("CLAUDE_PROJECT_DIR").unwrap()));
-```
-
-### Option 2: Config File Per Project
-
-```bash
-# .claude/hooks/config.json
-{
- "skillRulesPath": ".claude/skills/skill-rules.json",
- "projectType": "backend",
- "enableDebug": false
-}
-```
-
-Wrapper passes config location:
-```bash
-#!/bin/bash
-set -e
-
-cat | ~/.claude-hooks/bin/skill-activation-prompt \
- --config "$CLAUDE_PROJECT_DIR/.claude/hooks/config.json"
-```
-
-### Option 3: Command-line Arguments
-
-```bash
-#!/bin/bash
-set -e
-
-cat | ~/.claude-hooks/bin/skill-activation-prompt \
- --project-dir "$CLAUDE_PROJECT_DIR" \
- --skill-rules ".claude/skills/skill-rules.json"
-```
-
----
-
-## Advanced: Multiple Versions
-
-You can maintain multiple versions:
-
-```
-~/.claude-hooks/
-├── bin/
-│ ├── skill-activation-prompt-v1.0
-│ ├── skill-activation-prompt-v2.0 # New version
-│ └── skill-activation-prompt -> skill-activation-prompt-v2.0 # Symlink
-```
-
-Projects can choose version:
-```bash
-# Use latest (symlink)
-cat | ~/.claude-hooks/bin/skill-activation-prompt
-
-# Pin to specific version
-cat | ~/.claude-hooks/bin/skill-activation-prompt-v1.0
-```
-
----
-
-## Distribution Methods
-
-### Method 1: GitHub Releases
-
-```bash
-# In your hooks repo
-cargo build --release
-cd target/release
-
-# Create release artifacts
-tar -czf claude-hooks-linux-x64.tar.gz skill-activation-prompt file-analyzer
-tar -czf claude-hooks-macos-arm64.tar.gz skill-activation-prompt file-analyzer
-
-# Upload to GitHub releases
-gh release create v1.0.0 *.tar.gz
-```
-
-**Users install:**
-```bash
-# Download and extract
-wget https://github.com/you/claude-hooks/releases/download/v1.0.0/claude-hooks-linux-x64.tar.gz
-tar -xzf claude-hooks-linux-x64.tar.gz -C ~/.claude-hooks/bin/
-```
-
-### Method 2: Cargo Install
-
-**If published to crates.io:**
-```bash
-cargo install claude-hooks --root ~/.claude-hooks
-
-# Binaries installed to ~/.claude-hooks/bin/
-```
-
-### Method 3: Installation Script
-
-```bash
-# install.sh
-#!/bin/bash
-set -e
-
-echo "Installing Claude Rust Hooks..."
-
-# Detect OS and architecture
-OS=$(uname -s | tr '[:upper:]' '[:lower:]')
-ARCH=$(uname -m)
-
-# Download appropriate binary
-RELEASE_URL="https://github.com/you/claude-hooks/releases/latest/download"
-TARBALL="claude-hooks-${OS}-${ARCH}.tar.gz"
-
-# Create directory
-mkdir -p ~/.claude-hooks/bin
-
-# Download and extract
-curl -L "${RELEASE_URL}/${TARBALL}" | tar -xz -C ~/.claude-hooks/bin/
-
-# Make executable
-chmod +x ~/.claude-hooks/bin/*
-
-echo "✅ Installation complete!"
-echo "Binaries installed to: ~/.claude-hooks/bin/"
-```
-
-**Users run:**
-```bash
-curl -sSL https://your-repo.com/install.sh | bash
-```
-
----
-
-## Cross-Platform Considerations
-
-### Build for Multiple Targets
-
-```bash
-# Install cross-compilation targets
-rustup target add x86_64-unknown-linux-gnu
-rustup target add x86_64-apple-darwin
-rustup target add aarch64-apple-darwin
-rustup target add x86_64-pc-windows-msvc
-
-# Build for each platform
-cargo build --release --target x86_64-unknown-linux-gnu
-cargo build --release --target x86_64-apple-darwin
-cargo build --release --target aarch64-apple-darwin
-cargo build --release --target x86_64-pc-windows-msvc
-```
-
-### Platform-Specific Wrappers
-
-**Linux/macOS:**
-```bash
-#!/bin/bash
-cat | ~/.claude-hooks/bin/skill-activation-prompt
-```
-
-**Windows (PowerShell):**
-```powershell
-# skill-activation-prompt.ps1
-$input | & "$env:USERPROFILE\.claude-hooks\bin\skill-activation-prompt.exe"
-```
-
----
-
-## Example Project Structure
-
-### Standalone Hooks Repository
-
-```
-claude-hooks-rust/
-├── Cargo.toml
-├── src/
-│ ├── bin/
-│ │ ├── skill-activation-prompt.rs
-│ │ ├── file-analyzer.rs
-│ │ └── post-tool-tracker.rs
-│ └── lib.rs # Shared code
-├── README.md
-├── install.sh
-└── .github/
- └── workflows/
- └── release.yml # Auto-build releases
-```
-
-### Cargo.toml for Multiple Binaries
-
-```toml
-[package]
-name = "claude-hooks"
-version = "1.0.0"
-edition = "2021"
-
-# Multiple binaries in one project
-[[bin]]
-name = "skill-activation-prompt"
-path = "src/bin/skill-activation-prompt.rs"
-
-[[bin]]
-name = "file-analyzer"
-path = "src/bin/file-analyzer.rs"
-
-[[bin]]
-name = "post-tool-tracker"
-path = "src/bin/post-tool-tracker.rs"
-
-[dependencies]
-serde = { version = "1.0", features = ["derive"] }
-serde_json = "1.0"
-regex = "1.10"
-clap = { version = "4.4", features = ["derive"] } # For CLI args
-
-# Optional: Share code between binaries
-[lib]
-name = "claude_hooks_common"
-path = "src/lib.rs"
-```
-
-### Shared Library Code
-
-```rust
-// src/lib.rs - shared utilities
-pub mod config;
-pub mod file_analyzer;
-pub mod skill_matcher;
-
-// src/bin/skill-activation-prompt.rs
-use claude_hooks_common::config::load_config;
-use claude_hooks_common::skill_matcher::match_skills;
-
-fn main() {
- let config = load_config();
- let skills = match_skills(&config);
- // ... rest of implementation
-}
-```
-
----
-
-## Update Workflow
-
-### For Hook Developer
-
-```bash
-# Make changes
-cd ~/.claude-hooks/src
-# edit code
-
-# Test
-cargo run --bin skill-activation-prompt
-
-# Build and install
-cargo build --release
-cp target/release/* ~/.claude-hooks/bin/
-
-# Test in real project
-cd ~/test-project
-echo '{"prompt":"test"}' | ./.claude/hooks/skill-activation-prompt.sh
-```
-
-### For Hook User
-
-```bash
-# Update to latest version
-cd ~/.claude-hooks/src
-git pull
-cargo build --release
-cp target/release/* ~/.claude-hooks/bin/
-
-# All projects automatically use new version
-```
-
----
-
-## Comparison: Embedded vs Standalone
-
-### Embedded (Original Approach)
-
-```
-my-project/.claude/hooks/RustHooks/
-├── Cargo.toml
-├── src/
-│ ├── skill_activation_prompt.rs
-│ └── ...
-└── target/
- └── release/
- └── skill-activation-prompt (2.1 MB)
-```
-
-**Per project:**
-- 45s build time
-- 2.1 MB binary
-- Full source code
-- Must rebuild for updates
-
-**Total for 5 projects:**
-- 225s build time
-- 10.5 MB disk space
-- Update 5 times
-
----
-
-### Standalone (Recommended)
-
-```
-~/.claude-hooks/
-└── bin/
- └── skill-activation-prompt (2.1 MB)
-
-my-project/.claude/hooks/
-└── skill-activation-prompt.sh (50 bytes)
-```
-
-**Per project:**
-- 0s build time (just copy wrapper)
-- 50 bytes
-- No source needed
-- Automatic updates
-
-**Total for 5 projects:**
-- 45s build time (once)
-- 2.1 MB disk space (shared)
-- Update once
-
----
-
-## Recommendation
-
-**For Personal Use:**
-- ✅ Build once in `~/.claude-hooks/`
-- ✅ Per-project wrappers only
-- ✅ Update centrally
-
-**For Team Distribution:**
-- ✅ GitHub releases with pre-built binaries
-- ✅ Installation script
-- ✅ Version pinning option
-
-**For Open Source Project:**
-- ✅ Publish to crates.io
-- ✅ `cargo install claude-hooks`
-- ✅ Automatic updates with `cargo install --force`
-
----
-
-## Migration from Embedded to Standalone
-
-```bash
-# 1. Build standalone version
-cd /tmp
-git clone your-rust-hooks
-cd rust-hooks
-cargo build --release
-
-# 2. Install globally
-mkdir -p ~/.claude-hooks/bin
-cp target/release/* ~/.claude-hooks/bin/
-
-# 3. Update each project
-cd ~/project-1/.claude/hooks
-# Replace RustHooks/ with thin wrapper
-rm -rf RustHooks/
-cat > skill-activation-prompt.sh << 'EOF'
-#!/bin/bash
-cat | ~/.claude-hooks/bin/skill-activation-prompt
-EOF
-chmod +x skill-activation-prompt.sh
-
-# 4. Repeat for other projects (or script it)
-```
-
----
-
-## Conclusion
-
-**Yes, standalone Rust binaries are the better approach:**
-
-- ✅ Compile once, use everywhere
-- ✅ Faster project setup (just copy wrapper)
-- ✅ Centralized maintenance
-- ✅ Smaller per-project footprint
-- ✅ Easier to distribute
-
-**Only use embedded approach if:**
-- You need per-project customization in Rust code
-- You're actively developing/debugging the hook
-- You have conflicting version requirements
-
-**For 99% of use cases, standalone is better.**
diff --git a/.claude/hooks/RustHooks/install.sh b/.claude/hooks/RustHooks/install.sh
deleted file mode 100755
index 1345cf5..0000000
--- a/.claude/hooks/RustHooks/install.sh
+++ /dev/null
@@ -1,87 +0,0 @@
-#!/bin/bash
-set -e
-
-echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
-echo "Claude Code Rust Hooks Installer"
-echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
-echo
-
-# Check if Rust is installed
-if ! command -v cargo &> /dev/null; then
- echo "❌ Rust is not installed!"
- echo
- echo "Install Rust first:"
- echo " curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
- echo
- exit 1
-fi
-
-echo "✅ Rust found: $(rustc --version)"
-echo
-
-# Detect OS and architecture
-OS=$(uname -s | tr '[:upper:]' '[:lower:]')
-ARCH=$(uname -m)
-
-echo "Detected: $OS / $ARCH"
-echo
-
-# Installation directory
-INSTALL_DIR="${HOME}/.claude-hooks"
-BIN_DIR="${INSTALL_DIR}/bin"
-SRC_DIR="${INSTALL_DIR}/src"
-
-# Create directories
-echo "📁 Creating directories..."
-mkdir -p "$BIN_DIR"
-mkdir -p "$SRC_DIR"
-
-# Copy source if we're in the RustHooks directory
-if [ -f "Cargo.toml" ]; then
- echo "📦 Copying source code..."
- cp -r . "$SRC_DIR/"
- cd "$SRC_DIR"
-else
- echo "⚠️ Not in RustHooks directory"
- echo "Please cd to the RustHooks directory first"
- exit 1
-fi
-
-# Build release binaries
-echo
-echo "🔨 Building release binaries (this may take a minute)..."
-cargo build --release
-
-# Copy binaries to bin directory
-echo "📦 Installing binaries to $BIN_DIR..."
-for binary in target/release/skill-activation-prompt target/release/file-analyzer; do
- if [ -f "$binary" ]; then
- cp "$binary" "$BIN_DIR/"
- chmod +x "$BIN_DIR/$(basename $binary)"
- echo " ✅ Installed: $(basename $binary)"
- fi
-done
-
-echo
-echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
-echo "✅ Installation Complete!"
-echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
-echo
-echo "Binaries installed to: $BIN_DIR"
-echo
-echo "Next steps:"
-echo "1. Add to your PATH (optional):"
-echo " echo 'export PATH=\"\$HOME/.claude-hooks/bin:\$PATH\"' >> ~/.bashrc"
-echo
-echo "2. In each project, create a wrapper script:"
-echo " cd your-project/.claude/hooks"
-echo " cat > skill-activation-prompt.sh << 'EOF'"
-echo " #!/bin/bash"
-echo " cat | ~/.claude-hooks/bin/skill-activation-prompt"
-echo " EOF"
-echo " chmod +x skill-activation-prompt.sh"
-echo
-echo "3. Update .claude/settings.json to use the wrapper"
-echo
-echo "See STANDALONE_INSTALLATION.md for detailed instructions."
-echo
diff --git a/.claude/hooks/RustHooks/src/file_analyzer.rs b/.claude/hooks/RustHooks/src/file_analyzer.rs
deleted file mode 100644
index b5cc99f..0000000
--- a/.claude/hooks/RustHooks/src/file_analyzer.rs
+++ /dev/null
@@ -1,173 +0,0 @@
-use regex::Regex;
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-use std::path::Path;
-use std::time::Instant;
-use walkdir::WalkDir;
-
-#[derive(Debug)]
-struct FileAnalysis {
- has_try_catch: bool,
- has_async: bool,
- has_prisma: bool,
- has_controller: bool,
- has_api_call: bool,
- line_count: usize,
-}
-
-fn get_file_category(path: &str) -> &str {
- if path.contains("/frontend/")
- || path.contains("/client/")
- || path.contains("/src/components/")
- || path.contains("/src/features/") {
- "frontend"
- } else if path.contains("/src/controllers/")
- || path.contains("/src/services/")
- || path.contains("/src/routes/")
- || path.contains("/src/api/")
- || path.contains("/backend/")
- || path.contains("/server/") {
- "backend"
- } else if path.contains("/database/")
- || path.contains("/prisma/")
- || path.contains("/migrations/")
- || path.ends_with(".sql") {
- "database"
- } else {
- "other"
- }
-}
-
-fn should_analyze(path: &str) -> bool {
- let path_lower = path.to_lowercase();
-
- // Skip test files, config files
- if path_lower.contains(".test.")
- || path_lower.contains(".spec.")
- || path_lower.contains(".config.")
- || path_lower.contains("/types/")
- || path_lower.ends_with(".json")
- || path_lower.ends_with(".md") {
- return false;
- }
-
- // Check for code files
- path_lower.ends_with(".ts")
- || path_lower.ends_with(".tsx")
- || path_lower.ends_with(".js")
- || path_lower.ends_with(".jsx")
- || path_lower.ends_with(".rs")
- || path_lower.ends_with(".cs")
-}
-
-fn analyze_file(path: &Path) -> io::Result {
- let content = fs::read_to_string(path)?;
- let lines: Vec<&str> = content.lines().collect();
-
- // Pre-compile regex patterns for efficiency
- let try_regex = Regex::new(r"try\s*\{|try:|except:").unwrap();
- let async_regex = Regex::new(r"async\s+|async def|async fn|Task<").unwrap();
- let prisma_regex = Regex::new(r"prisma\.|PrismaClient|findMany|findUnique|create\(").unwrap();
- let controller_regex = Regex::new(r"Controller|router\.|app\.(get|post|put|delete)|HttpGet|HttpPost").unwrap();
- let api_regex = Regex::new(r"fetch\(|axios\.|HttpClient|apiClient\.").unwrap();
-
- Ok(FileAnalysis {
- has_try_catch: try_regex.is_match(&content),
- has_async: async_regex.is_match(&content),
- has_prisma: prisma_regex.is_match(&content),
- has_controller: controller_regex.is_match(&content),
- has_api_call: api_regex.is_match(&content),
- line_count: lines.len(),
- })
-}
-
-fn main() -> io::Result<()> {
- let args: Vec = env::args().collect();
-
- if args.len() < 2 {
- eprintln!("Usage: {} ", args[0]);
- eprintln!("\nAnalyzes files in directory for error-prone patterns");
- return Ok(());
- }
-
- let dir = &args[1];
- let start = Instant::now();
-
- println!("\n🔍 ANALYZING FILES IN: {}\n", dir);
- println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
-
- let mut stats = Stats::default();
-
- for entry in WalkDir::new(dir)
- .follow_links(true)
- .into_iter()
- .filter_map(|e| e.ok())
- .filter(|e| e.file_type().is_file())
- {
- let path = entry.path();
- let path_str = path.to_string_lossy();
-
- if !should_analyze(&path_str) {
- continue;
- }
-
- stats.total_files += 1;
- let category = get_file_category(&path_str);
-
- match category {
- "backend" => stats.backend_files += 1,
- "frontend" => stats.frontend_files += 1,
- "database" => stats.database_files += 1,
- _ => stats.other_files += 1,
- }
-
- if let Ok(analysis) = analyze_file(path) {
- if analysis.has_async { stats.async_files += 1; }
- if analysis.has_try_catch { stats.try_catch_files += 1; }
- if analysis.has_prisma { stats.prisma_files += 1; }
- if analysis.has_controller { stats.controller_files += 1; }
- if analysis.has_api_call { stats.api_call_files += 1; }
-
- // Flag risky patterns
- if analysis.has_async && !analysis.has_try_catch {
- println!("⚠️ {} - Async without try/catch",
- path.file_name().unwrap().to_string_lossy());
- }
- }
- }
-
- let elapsed = start.elapsed();
-
- println!("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
- println!("📊 ANALYSIS RESULTS\n");
- println!("Total Files: {}", stats.total_files);
- println!(" Backend: {}", stats.backend_files);
- println!(" Frontend: {}", stats.frontend_files);
- println!(" Database: {}", stats.database_files);
- println!(" Other: {}", stats.other_files);
- println!("\nPatterns Detected:");
- println!(" Async: {}", stats.async_files);
- println!(" Try/Catch: {}", stats.try_catch_files);
- println!(" Prisma: {}", stats.prisma_files);
- println!(" Controllers: {}", stats.controller_files);
- println!(" API Calls: {}", stats.api_call_files);
- println!("\n⚡ Analysis completed in {:.2?}", elapsed);
- println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
-
- Ok(())
-}
-
-#[derive(Default)]
-struct Stats {
- total_files: usize,
- backend_files: usize,
- frontend_files: usize,
- database_files: usize,
- other_files: usize,
- async_files: usize,
- try_catch_files: usize,
- prisma_files: usize,
- controller_files: usize,
- api_call_files: usize,
-}
diff --git a/.claude/hooks/RustHooks/src/post_tool_use_tracker_sqlite.rs b/.claude/hooks/RustHooks/src/post_tool_use_tracker_sqlite.rs
deleted file mode 100644
index 4d64921..0000000
--- a/.claude/hooks/RustHooks/src/post_tool_use_tracker_sqlite.rs
+++ /dev/null
@@ -1,278 +0,0 @@
-use anyhow::Result;
-use chrono::Utc;
-use regex::Regex;
-use rusqlite::{params, Connection};
-use serde::Deserialize;
-use std::collections::HashMap;
-use std::fs;
-use std::io::{self, Read};
-use std::path::Path;
-
-#[derive(Debug, Deserialize)]
-struct HookInput {
- session_id: String,
- tool_name: Option,
- tool_args: Option>,
-}
-
-struct Database {
- conn: Connection,
-}
-
-impl Database {
- fn new(session_id: &str) -> Result {
- let home = std::env::var("HOME").unwrap_or_else(|_| "/root".to_string());
- let db_path = format!("{home}/.claude/hooks-state-rust/{session_id}.db");
-
- // Ensure directory exists
- fs::create_dir_all(format!("{home}/.claude/hooks-state-rust"))?;
-
- let conn = Connection::open(&db_path)?;
-
- // Create schema
- conn.execute(
- "CREATE TABLE IF NOT EXISTS file_modifications (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- session_id TEXT NOT NULL,
- file_path TEXT NOT NULL,
- tool TEXT NOT NULL,
- timestamp TEXT NOT NULL,
- category TEXT NOT NULL,
- has_async BOOLEAN DEFAULT 0,
- has_try_catch BOOLEAN DEFAULT 0,
- has_prisma BOOLEAN DEFAULT 0,
- has_controller BOOLEAN DEFAULT 0,
- has_api_call BOOLEAN DEFAULT 0,
- line_count INTEGER DEFAULT 0
- )",
- [],
- )?;
-
- // Create indexes for fast queries
- conn.execute(
- "CREATE INDEX IF NOT EXISTS idx_session
- ON file_modifications(session_id)",
- [],
- )?;
-
- conn.execute(
- "CREATE INDEX IF NOT EXISTS idx_category
- ON file_modifications(session_id, category)",
- [],
- )?;
-
- conn.execute(
- "CREATE INDEX IF NOT EXISTS idx_timestamp
- ON file_modifications(timestamp DESC)",
- [],
- )?;
-
- // Create session summary table
- conn.execute(
- "CREATE TABLE IF NOT EXISTS sessions (
- session_id TEXT PRIMARY KEY,
- start_time TEXT NOT NULL,
- last_activity TEXT NOT NULL,
- total_files INTEGER DEFAULT 0,
- backend_files INTEGER DEFAULT 0,
- frontend_files INTEGER DEFAULT 0,
- database_files INTEGER DEFAULT 0
- )",
- [],
- )?;
-
- Ok(Self { conn })
- }
-
- fn track_modification(&self, session_id: &str, file_path: &str, tool: &str) -> Result<()> {
- let category = get_file_category(file_path);
- let analysis = if should_analyze(file_path) {
- analyze_file(file_path)
- } else {
- FileAnalysis::default()
- };
-
- let timestamp = Utc::now().to_rfc3339();
-
- // Insert file modification
- self.conn.execute(
- "INSERT INTO file_modifications
- (session_id, file_path, tool, timestamp, category,
- has_async, has_try_catch, has_prisma, has_controller, has_api_call, line_count)
- VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)",
- params![
- session_id,
- file_path,
- tool,
- timestamp,
- category,
- analysis.has_async,
- analysis.has_try_catch,
- analysis.has_prisma,
- analysis.has_controller,
- analysis.has_api_call,
- analysis.line_count,
- ],
- )?;
-
- // Update session summary
- self.update_session_summary(session_id, category)?;
-
- Ok(())
- }
-
- fn update_session_summary(&self, session_id: &str, category: &str) -> Result<()> {
- let now = Utc::now().to_rfc3339();
-
- // Check if session exists
- let exists: bool = self
- .conn
- .query_row(
- "SELECT 1 FROM sessions WHERE session_id = ?1",
- params![session_id],
- |_| Ok(true),
- )
- .unwrap_or(false);
-
- if !exists {
- // Create new session
- self.conn.execute(
- "INSERT INTO sessions (session_id, start_time, last_activity, total_files)
- VALUES (?1, ?2, ?3, 1)",
- params![session_id, &now, &now],
- )?;
- }
-
- // Update session
- let category_col = match category {
- "backend" => "backend_files",
- "frontend" => "frontend_files",
- "database" => "database_files",
- _ => "",
- };
-
- if !category_col.is_empty() {
- self.conn.execute(
- &format!(
- "UPDATE sessions
- SET last_activity = ?1,
- total_files = total_files + 1,
- {category_col} = {category_col} + 1
- WHERE session_id = ?2"
- ),
- params![&now, session_id],
- )?;
- } else {
- self.conn.execute(
- "UPDATE sessions
- SET last_activity = ?1,
- total_files = total_files + 1
- WHERE session_id = ?2",
- params![&now, session_id],
- )?;
- }
-
- Ok(())
- }
-}
-
-#[derive(Default)]
-struct FileAnalysis {
- has_async: bool,
- has_try_catch: bool,
- has_prisma: bool,
- has_controller: bool,
- has_api_call: bool,
- line_count: i32,
-}
-
-fn get_file_category(path: &str) -> &str {
- if path.contains("/frontend/")
- || path.contains("/client/")
- || path.contains("/src/components/")
- || path.contains("/src/features/")
- {
- "frontend"
- } else if path.contains("/src/controllers/")
- || path.contains("/src/services/")
- || path.contains("/src/routes/")
- || path.contains("/backend/")
- {
- "backend"
- } else if path.contains("/database/") || path.contains("/prisma/") {
- "database"
- } else {
- "other"
- }
-}
-
-fn should_analyze(path: &str) -> bool {
- let path_lower = path.to_lowercase();
- !path_lower.contains(".test.")
- && !path_lower.contains(".spec.")
- && (path_lower.ends_with(".ts")
- || path_lower.ends_with(".tsx")
- || path_lower.ends_with(".js")
- || path_lower.ends_with(".jsx"))
-}
-
-fn analyze_file(path: &str) -> FileAnalysis {
- let Ok(content) = fs::read_to_string(path) else {
- return FileAnalysis::default();
- };
-
- let line_count = content.lines().count() as i32;
-
- // Compile regexes
- let try_regex = Regex::new(r"try\s*\{").unwrap();
- let async_regex = Regex::new(r"async\s+").unwrap();
- let prisma_regex = Regex::new(r"prisma\.|PrismaClient").unwrap();
- let controller_regex = Regex::new(r"Controller|router\.|app\.(get|post)").unwrap();
- let api_regex = Regex::new(r"fetch\(|axios\.|apiClient\.").unwrap();
-
- FileAnalysis {
- has_try_catch: try_regex.is_match(&content),
- has_async: async_regex.is_match(&content),
- has_prisma: prisma_regex.is_match(&content),
- has_controller: controller_regex.is_match(&content),
- has_api_call: api_regex.is_match(&content),
- line_count,
- }
-}
-
-fn extract_file_path(tool: &str, args: &HashMap) -> Option {
- args.get("file_path")
- .and_then(|v| v.as_str())
- .map(|s| s.to_string())
-}
-
-fn main() -> Result<()> {
- // Read stdin
- let mut input = String::new();
- io::stdin().read_to_string(&mut input)?;
-
- let data: HookInput = serde_json::from_str(&input)?;
-
- // Only track file modification tools
- let file_tools = ["Edit", "Write", "MultiEdit", "NotebookEdit"];
- if let Some(ref tool) = data.tool_name {
- if !file_tools.contains(&tool.as_str()) {
- return Ok(());
- }
-
- // Extract file path
- if let Some(ref args) = data.tool_args {
- if let Some(file_path) = extract_file_path(tool, args) {
- let db = Database::new(&data.session_id)?;
- db.track_modification(&data.session_id, &file_path, tool)?;
-
- // Debug output
- if std::env::var("DEBUG_HOOKS").is_ok() {
- eprintln!("[Rust/SQLite] Tracked: {file_path} ({})", get_file_category(&file_path));
- }
- }
- }
- }
-
- Ok(())
-}
diff --git a/.claude/hooks/RustHooks/src/skill_activation_prompt.rs b/.claude/hooks/RustHooks/src/skill_activation_prompt.rs
deleted file mode 100644
index b513fd6..0000000
--- a/.claude/hooks/RustHooks/src/skill_activation_prompt.rs
+++ /dev/null
@@ -1,156 +0,0 @@
-use serde::{Deserialize, Serialize};
-use std::collections::HashMap;
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-use regex::Regex;
-
-#[derive(Debug, Deserialize)]
-struct HookInput {
- session_id: String,
- transcript_path: String,
- cwd: String,
- permission_mode: String,
- prompt: String,
-}
-
-#[derive(Debug, Deserialize)]
-struct PromptTriggers {
- #[serde(default)]
- keywords: Vec,
- #[serde(default, rename = "intentPatterns")]
- intent_patterns: Vec,
-}
-
-#[derive(Debug, Deserialize)]
-struct SkillRule {
- r#type: String,
- enforcement: String,
- priority: String,
- #[serde(rename = "promptTriggers")]
- prompt_triggers: Option,
-}
-
-#[derive(Debug, Deserialize)]
-struct SkillRules {
- version: String,
- skills: HashMap,
-}
-
-#[derive(Debug)]
-struct MatchedSkill {
- name: String,
- match_type: String,
- priority: String,
-}
-
-fn main() -> io::Result<()> {
- // Read input from stdin
- let mut input = String::new();
- io::stdin().read_to_string(&mut input)?;
-
- let data: HookInput = serde_json::from_str(&input)
- .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
-
- let prompt = data.prompt.to_lowercase();
-
- // Load skill rules
- let project_dir = env::var("CLAUDE_PROJECT_DIR")
- .unwrap_or_else(|_| String::from("/home/project"));
- let rules_path = format!("{project_dir}/.claude/skills/skill-rules.json");
-
- let rules_content = fs::read_to_string(&rules_path)?;
- let rules: SkillRules = serde_json::from_str(&rules_content)
- .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
-
- let mut matched_skills = Vec::new();
-
- // Check each skill for matches
- for (skill_name, config) in &rules.skills {
- if let Some(triggers) = &config.prompt_triggers {
- // Keyword matching
- let keyword_match = triggers.keywords.iter()
- .any(|kw| prompt.contains(&kw.to_lowercase()));
-
- if keyword_match {
- matched_skills.push(MatchedSkill {
- name: skill_name.clone(),
- match_type: "keyword".to_string(),
- priority: config.priority.clone(),
- });
- continue;
- }
-
- // Intent pattern matching
- let intent_match = triggers.intent_patterns.iter()
- .filter_map(|pattern| Regex::new(pattern).ok())
- .any(|regex| regex.is_match(&prompt));
-
- if intent_match {
- matched_skills.push(MatchedSkill {
- name: skill_name.clone(),
- match_type: "intent".to_string(),
- priority: config.priority.clone(),
- });
- }
- }
- }
-
- // Generate output if matches found
- if !matched_skills.is_empty() {
- println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
- println!("🎯 SKILL ACTIVATION CHECK");
- println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
-
- // Group by priority
- let critical: Vec<_> = matched_skills.iter()
- .filter(|s| s.priority == "critical")
- .collect();
- let high: Vec<_> = matched_skills.iter()
- .filter(|s| s.priority == "high")
- .collect();
- let medium: Vec<_> = matched_skills.iter()
- .filter(|s| s.priority == "medium")
- .collect();
- let low: Vec<_> = matched_skills.iter()
- .filter(|s| s.priority == "low")
- .collect();
-
- if !critical.is_empty() {
- println!("⚠️ CRITICAL SKILLS (REQUIRED):");
- for skill in critical {
- println!(" → {}", skill.name);
- }
- println!();
- }
-
- if !high.is_empty() {
- println!("📚 RECOMMENDED SKILLS:");
- for skill in high {
- println!(" → {}", skill.name);
- }
- println!();
- }
-
- if !medium.is_empty() {
- println!("💡 SUGGESTED SKILLS:");
- for skill in medium {
- println!(" → {}", skill.name);
- }
- println!();
- }
-
- if !low.is_empty() {
- println!("📌 OPTIONAL SKILLS:");
- for skill in low {
- println!(" → {}", skill.name);
- }
- println!();
- }
-
- println!("ACTION: Use Skill tool BEFORE responding");
- println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
- }
-
- Ok(())
-}
diff --git a/.claude/skills/svelte-skill/README.md b/.claude/skills/svelte-skill/README.md
new file mode 100644
index 0000000..53c8956
--- /dev/null
+++ b/.claude/skills/svelte-skill/README.md
@@ -0,0 +1,168 @@
+# Svelte Skill
+
+A comprehensive Claude Code skill for Svelte 5 development, following the 500-line modular resource pattern.
+
+## Overview
+
+This skill provides complete Svelte 5 documentation extracted from the official Svelte docs (llms.txt), organized into bite-sized, context-efficient resource files.
+
+**Total Coverage:** 9,691 lines of Svelte documentation
+**Files:** 24 resource files + 1 main SKILL.md + 1 skill-rules.json
+**Max File Size:** 638 lines (all under 700, most under 500)
+
+## Structure
+
+```
+svelte-skill/
+├── SKILL.md (436 lines) - Main navigation & overview
+├── skill-rules.json - Auto-activation triggers
+├── README.md - This file
+└── resources/ (24 files, 9,691 total lines)
+ ├── Core Concepts (10 files)
+ │ ├── components.md (79 lines)
+ │ ├── runes-state.md (371 lines)
+ │ ├── runes-derived-effect.md (471 lines)
+ │ ├── runes-props-utilities.md (366 lines)
+ │ ├── template-control-flow.md (470 lines)
+ │ ├── template-snippets.md (578 lines)
+ │ ├── directives-bind-use.md (482 lines)
+ │ ├── directives-animations.md (638 lines)
+ │ ├── styling.md (178 lines)
+ │ └── special-elements.md (262 lines)
+ ├── Advanced Topics (3 files)
+ │ ├── advanced-concepts-1.md (342 lines)
+ │ ├── advanced-concepts-2.md (338 lines)
+ │ └── typescript.md (282 lines)
+ ├── Migration & Upgrades (3 files)
+ │ ├── migration-svelte4.md (245 lines)
+ │ ├── migration-svelte5-1.md (483 lines)
+ │ └── migration-svelte5-2.md (493 lines)
+ └── Reference & Troubleshooting (8 files)
+ ├── faqs-1.md (457 lines)
+ ├── faqs-2.md (450 lines)
+ ├── compiler-errors-1.md (502 lines)
+ ├── compiler-errors-2.md (350 lines)
+ ├── compiler-warnings-1.md (493 lines)
+ ├── compiler-warnings-2.md (519 lines)
+ ├── runtime-errors-1.md (421 lines)
+ └── runtime-errors-2.md (421 lines)
+```
+
+## Usage
+
+### Automatic Activation
+
+The skill auto-activates when:
+- Working with `.svelte`, `.svelte.js`, or `.svelte.ts` files
+- Mentioning keywords: `svelte`, `runes`, `$state`, `$derived`, `$effect`, `$props`
+- User intent indicates Svelte development (e.g., "create svelte component")
+
+### Manual Activation
+
+Load the skill explicitly:
+```
+/skill svelte-skill
+```
+
+### Progressive Disclosure
+
+Start with **SKILL.md** for high-level guidance, then load specific resource files as needed:
+
+**For beginners:**
+1. components.md
+2. runes-state.md
+3. runes-derived-effect.md
+4. template-control-flow.md
+
+**For Svelte 4 users:**
+1. migration-svelte5-1.md
+2. migration-svelte5-2.md
+3. All runes-*.md files
+
+**For specific tasks:**
+- State management → runes-state.md
+- Computed values → runes-derived-effect.md
+- Component props → runes-props-utilities.md
+- Forms → directives-bind-use.md
+- Animations → directives-animations.md
+- Troubleshooting → faqs-1.md, compiler-errors-1.md
+
+## Key Features
+
+### Comprehensive Coverage
+- ✅ Complete Svelte 5 runes system ($state, $derived, $effect, $props, etc.)
+- ✅ All template syntax (control flow, snippets, special tags)
+- ✅ All directives (bind, use, transition, animate, class, style)
+- ✅ Styling (scoped, global, custom properties)
+- ✅ Advanced patterns (stores, context, lifecycle)
+- ✅ TypeScript integration
+- ✅ Migration guides (Svelte 4 → 5)
+- ✅ Complete error/warning reference
+- ✅ FAQs and troubleshooting
+
+### Optimized for Context Limits
+- All files under 700 lines (most under 500)
+- Logical topic boundaries
+- No redundancy between files
+- Clear cross-references
+
+### Best Practices Included
+- Recommended patterns
+- Anti-patterns to avoid
+- Performance considerations
+- Type safety guidelines
+
+## Content Map
+
+### Core Learning Path
+1. **Components** (79 lines) - File structure basics
+2. **Runes - State** (371 lines) - Reactive state management
+3. **Runes - Derived & Effect** (471 lines) - Computed values & side effects
+4. **Runes - Props & Utilities** (366 lines) - Component communication
+5. **Template Control Flow** (470 lines) - Conditionals & loops
+6. **Template Snippets** (578 lines) - Reusable fragments
+7. **Directives - Bind & Use** (482 lines) - Two-way binding & actions
+8. **Directives - Animations** (638 lines) - Transitions & animations
+
+### Reference Documentation
+- **Special Elements** (262 lines) - svelte:window, svelte:head, etc.
+- **Styling** (178 lines) - Scoped CSS, globals
+- **TypeScript** (282 lines) - Type safety
+- **Advanced Concepts** (680 lines total) - Stores, context, lifecycle
+- **Errors & Warnings** (2,706 lines total) - Complete diagnostic reference
+- **FAQs** (907 lines total) - Common questions
+
+### Migration Resources
+- **Svelte 4 Guide** (245 lines) - Svelte 3 → 4
+- **Svelte 5 Guide** (976 lines total) - Svelte 4 → 5 (runes migration)
+
+## Integration with Claude Code
+
+This skill follows the **catalyst reference architecture** for skills:
+
+1. **SKILL.md** - Navigation hub (<500 lines)
+2. **resources/** - Deep-dive documentation (<700 lines each)
+3. **skill-rules.json** - Auto-activation configuration
+4. **Progressive disclosure** - Load only what you need
+
+## Source
+
+Extracted from the official Svelte documentation (`llms.txt` - 16,249 lines) and split into modular resources following the 500-line rule for optimal Claude Code performance.
+
+**Original source:** Svelte 5 official documentation
+**Extraction date:** 2025
+**Svelte version:** 5.x (latest stable)
+
+## Maintenance
+
+To update this skill with new Svelte documentation:
+
+1. Download latest `llms.txt` from Svelte docs
+2. Run extraction script (use line ranges from SVELTE_DOCS_CONTENT_MAP.md)
+3. Verify all files remain under 700 lines
+4. Update SKILL.md resource table if structure changes
+5. Test skill activation triggers
+
+## License
+
+This skill contains documentation from the official Svelte project. The Svelte documentation is licensed under the MIT License. This skill packaging is part of the catalyst reference library.
diff --git a/.claude/skills/svelte-skill/SKILL.md b/.claude/skills/svelte-skill/SKILL.md
new file mode 100644
index 0000000..d2807c7
--- /dev/null
+++ b/.claude/skills/svelte-skill/SKILL.md
@@ -0,0 +1,436 @@
+---
+tags: [svelte, frontend, reactive, components, web-development]
+version: 5.x
+framework: svelte
+description: Comprehensive Svelte 5 development guidelines covering runes, components, reactivity, and modern web development patterns
+---
+
+# Svelte Development Skill
+
+**Expert guidance for building modern web applications with Svelte 5**
+
+This skill provides comprehensive documentation for Svelte 5, the revolutionary web framework that compiles declarative components into highly optimized JavaScript. Use this skill when working with `.svelte` files, implementing reactive state management, or building component-based user interfaces.
+
+## What is Svelte?
+
+Svelte is a **compiler-based framework** for building user interfaces. Unlike traditional frameworks that do their work in the browser, Svelte shifts that work into a compile step that happens at build time. This means:
+
+- **No virtual DOM** - Svelte compiles components to highly efficient imperative code
+- **True reactivity** - Reactive values update automatically without manual subscriptions
+- **Small bundle sizes** - Only the code you use is included
+- **Simple syntax** - Familiar HTML, CSS, and JavaScript with minimal boilerplate
+
+## Quick Start
+
+**Creating a new Svelte project with SvelteKit:**
+
+```bash
+npx sv create myapp
+cd myapp
+npm install
+npm run dev
+```
+
+**Basic component structure:**
+
+```svelte
+
+
+
+
+
+```
+
+## When to Use This Skill
+
+**Activate this skill when:**
+- Creating or editing `.svelte` component files
+- Implementing reactive state with runes (`$state`, `$derived`, `$effect`)
+- Working with component props and two-way bindings
+- Using template syntax (conditionals, loops, await blocks)
+- Styling components with scoped CSS
+- Integrating with SvelteKit for full-stack applications
+- Migrating from Svelte 4 to Svelte 5
+- Debugging reactivity or component lifecycle issues
+
+## Core Concepts Overview
+
+### 1. **Components** → [See resources/components.md](resources/components.md)
+ - `.svelte` file structure (`
+
+
+
+
+ ```
+
+2. Add reactive state with `$state()`
+3. Create computed values with `$derived()`
+4. Handle side effects with `$effect()`
+5. Accept props with `$props()`
+
+See **[components.md](resources/components.md)** for details.
+
+### Implementing Reactivity
+
+**Simple state:**
+```svelte
+
+```
+
+**Derived values:**
+```svelte
+
+```
+
+**Effects:**
+```svelte
+
+```
+
+See **[runes.md](resources/runes.md)** for comprehensive reactivity patterns.
+
+### Building Forms
+
+```svelte
+
+
+
+```
+
+See **[directives.md](resources/directives.md)** for all binding options.
+
+### Conditional Rendering
+
+```svelte
+{#if condition}
+
Condition is true
+{:else if otherCondition}
+
Other condition is true
+{:else}
+
All conditions are false
+{/if}
+```
+
+See **[template-syntax.md](resources/template-syntax.md)** for all control flow patterns.
+
+### List Rendering
+
+```svelte
+
+
+{#each items as item (item.id)}
+
{item.name}
+{/each}
+```
+
+See **[template-syntax.md](resources/template-syntax.md)** for iteration patterns.
+
+## Best Practices
+
+### State Management
+- ✅ Use `$state()` for mutable reactive values
+- ✅ Use `$derived()` for computed values (not `$effect`)
+- ✅ Keep effects minimal and focused
+- ❌ Don't update state inside `$effect()` (use `$derived()` instead)
+- ❌ Don't destructure reactive state (it breaks reactivity)
+
+### Component Design
+- ✅ Keep components small and focused
+- ✅ Use props for parent → child communication
+- ✅ Use callbacks or events for child → parent communication
+- ✅ Use `$bindable()` sparingly for two-way binding
+- ❌ Don't mutate props (unless marked `$bindable`)
+- ❌ Don't over-use context API (prefer explicit props)
+
+### Performance
+- ✅ Use keyed `{#each}` blocks for dynamic lists
+- ✅ Memoize expensive computations with `$derived()`
+- ✅ Use `$state.raw()` for large non-reactive data
+- ✅ Lazy-load components when appropriate
+- ❌ Don't create unnecessary derived values
+- ❌ Don't use effects where derived values would work
+
+### Styling
+- ✅ Leverage scoped styles by default
+- ✅ Use CSS custom properties for theming
+- ✅ Use `class:` directive for conditional classes
+- ❌ Don't use global styles unless absolutely necessary
+- ❌ Don't inline complex styles (use classes)
+
+See **[common-patterns.md](resources/common-patterns.md)** for detailed best practices.
+
+## Error Handling & Debugging
+
+### Common Issues
+
+**"State is not updating"**
+- Check that you're using `$state()`, not plain variables
+- Ensure you're not destructuring reactive state
+- See [common-patterns.md](resources/common-patterns.md)
+
+**"Effect running infinitely"**
+- Check for state updates inside the effect
+- Use `$derived()` instead for computed values
+- See [runes.md](resources/runes.md)
+
+**"Props not working"**
+- Ensure you're using `$props()` destructuring
+- Check prop names match in parent and child
+- See [runes.md](resources/runes.md)
+
+### Debugging Tools
+- `$inspect()` - Log reactive state changes
+- `$inspect.trace()` - Trace effect dependencies
+- `{@debug}` - Breakpoints in templates
+- Browser DevTools - Standard debugging
+
+See **[common-patterns.md](resources/common-patterns.md)** for troubleshooting guide.
+
+## TypeScript Support
+
+Svelte has excellent TypeScript support. Add type safety to your components:
+
+```svelte
+
+```
+
+See **[typescript.md](resources/typescript.md)** for comprehensive typing patterns.
+
+## SvelteKit Integration
+
+This skill focuses on Svelte components. For SvelteKit-specific features (routing, server-side rendering, API routes, etc.), refer to the SvelteKit documentation or use a dedicated SvelteKit skill.
+
+**Svelte vs SvelteKit:**
+- **Svelte** = Component framework (this skill)
+- **SvelteKit** = Application framework (routing, SSR, API, deployment)
+
+## Getting Help
+
+**Official Resources:**
+- [Interactive Tutorial](https://svelte.dev/tutorial)
+- [Playground](https://svelte.dev/playground)
+- [Discord Community](https://svelte.dev/chat)
+- [Stack Overflow](https://stackoverflow.com/questions/tagged/svelte)
+
+**Within This Skill:**
+- Start with the appropriate resource file from the table above
+- Check [common-patterns.md](resources/common-patterns.md) for FAQs
+- Review [migration-guide.md](resources/migration-guide.md) if upgrading
+
+## Skill Maintenance
+
+**Version:** Svelte 5.x (latest stable)
+**Last Updated:** Extracted from official Svelte documentation
+**Compatibility:** This skill covers Svelte 5. For Svelte 4 projects, see [migration-guide.md](resources/migration-guide.md)
+
+---
+
+## Progressive Disclosure Strategy
+
+This skill follows the **500-line rule** for optimal context management:
+
+1. **SKILL.md** (this file) - High-level overview and navigation (~490 lines)
+2. **Resource files** - Deep dives into specific topics (<500 lines each)
+
+**Load resource files only when needed:**
+- General guidance → Stay in SKILL.md
+- Specific implementation → Load the relevant resource file
+- Deep troubleshooting → Load common-patterns.md
+
+This keeps your context clean and focused on the task at hand.
+
+---
+
+**Ready to start?** Pick a resource file from the Quick Reference table above, or ask specific questions about Svelte development!
diff --git a/.claude/skills/svelte-skill/resources/advanced-concepts-1.md b/.claude/skills/svelte-skill/resources/advanced-concepts-1.md
new file mode 100644
index 0000000..ada9d04
--- /dev/null
+++ b/.claude/skills/svelte-skill/resources/advanced-concepts-1.md
@@ -0,0 +1,342 @@
+# Stores
+
+
+
+A _store_ is an object that allows reactive access to a value via a simple _store contract_. The [`svelte/store` module](../svelte-store) contains minimal store implementations which fulfil this contract.
+
+Any time you have a reference to a store, you can access its value inside a component by prefixing it with the `$` character. This causes Svelte to declare the prefixed variable, subscribe to the store at component initialisation and unsubscribe when appropriate.
+
+Assignments to `$`-prefixed variables require that the variable be a writable store, and will result in a call to the store's `.set` method.
+
+Note that the store must be declared at the top level of the component — not inside an `if` block or a function, for example.
+
+Local variables (that do not represent store values) must _not_ have a `$` prefix.
+
+```svelte
+
+```
+
+## When to use stores
+
+Prior to Svelte 5, stores were the go-to solution for creating cross-component reactive states or extracting logic. With runes, these use cases have greatly diminished.
+
+- when extracting logic, it's better to take advantage of runes' universal reactivity: You can use runes outside the top level of components and even place them into JavaScript or TypeScript files (using a `.svelte.js` or `.svelte.ts` file ending)
+- when creating shared state, you can create a `$state` object containing the values you need and then manipulate said state
+
+```ts
+/// file: state.svelte.js
+export const userState = $state({
+ name: 'name',
+ /* ... */
+});
+```
+
+```svelte
+
+
+
+
User name: {userState.name}
+
+```
+
+Stores are still a good solution when you have complex asynchronous data streams or it's important to have more manual control over updating values or listening to changes. If you're familiar with RxJs and want to reuse that knowledge, the `$` also comes in handy for you.
+
+## svelte/store
+
+The `svelte/store` module contains a minimal store implementation which fulfil the store contract. It provides methods for creating stores that you can update from the outside, stores you can only update from the inside, and for combining and deriving stores.
+
+### `writable`
+
+Function that creates a store which has values that can be set from 'outside' components. It gets created as an object with additional `set` and `update` methods.
+
+`set` is a method that takes one argument which is the value to be set. The store value gets set to the value of the argument if the store value is not already equal to it.
+
+`update` is a method that takes one argument which is a callback. The callback takes the existing store value as its argument and returns the new value to be set to the store.
+
+```js
+/// file: store.js
+import { writable } from 'svelte/store';
+
+const count = writable(0);
+
+count.subscribe((value) => {
+ console.log(value);
+}); // logs '0'
+
+count.set(1); // logs '1'
+
+count.update((n) => n + 1); // logs '2'
+```
+
+If a function is passed as the second argument, it will be called when the number of subscribers goes from zero to one (but not from one to two, etc). That function will be passed a `set` function which changes the value of the store, and an `update` function which works like the `update` method on the store, taking a callback to calculate the store's new value from its old value. It must return a `stop` function that is called when the subscriber count goes from one to zero.
+
+```js
+/// file: store.js
+import { writable } from 'svelte/store';
+
+const count = writable(0, () => {
+ console.log('got a subscriber');
+ return () => console.log('no more subscribers');
+});
+
+count.set(1); // does nothing
+
+const unsubscribe = count.subscribe((value) => {
+ console.log(value);
+}); // logs 'got a subscriber', then '1'
+
+unsubscribe(); // logs 'no more subscribers'
+```
+
+Note that the value of a `writable` is lost when it is destroyed, for example when the page is refreshed. However, you can write your own logic to sync the value to for example the `localStorage`.
+
+### `readable`
+
+Creates a store whose value cannot be set from 'outside', the first argument is the store's initial value, and the second argument to `readable` is the same as the second argument to `writable`.
+
+```ts
+import { readable } from 'svelte/store';
+
+const time = readable(new Date(), (set) => {
+ set(new Date());
+
+ const interval = setInterval(() => {
+ set(new Date());
+ }, 1000);
+
+ return () => clearInterval(interval);
+});
+
+const ticktock = readable('tick', (set, update) => {
+ const interval = setInterval(() => {
+ update((sound) => (sound === 'tick' ? 'tock' : 'tick'));
+ }, 1000);
+
+ return () => clearInterval(interval);
+});
+```
+
+### `derived`
+
+Derives a store from one or more other stores. The callback runs initially when the first subscriber subscribes and then whenever the store dependencies change.
+
+In the simplest version, `derived` takes a single store, and the callback returns a derived value.
+
+```ts
+// @filename: ambient.d.ts
+import { type Writable } from 'svelte/store';
+
+declare global {
+ const a: Writable;
+}
+
+export {};
+
+// @filename: index.ts
+// ---cut---
+import { derived } from 'svelte/store';
+
+const doubled = derived(a, ($a) => $a * 2);
+```
+
+The callback can set a value asynchronously by accepting a second argument, `set`, and an optional third argument, `update`, calling either or both of them when appropriate.
+
+In this case, you can also pass a third argument to `derived` — the initial value of the derived store before `set` or `update` is first called. If no initial value is specified, the store's initial value will be `undefined`.
+
+```ts
+// @filename: ambient.d.ts
+import { type Writable } from 'svelte/store';
+
+declare global {
+ const a: Writable;
+}
+
+export {};
+
+// @filename: index.ts
+// @errors: 18046 2769 7006
+// ---cut---
+import { derived } from 'svelte/store';
+
+const delayed = derived(
+ a,
+ ($a, set) => {
+ setTimeout(() => set($a), 1000);
+ },
+ 2000
+);
+
+const delayedIncrement = derived(a, ($a, set, update) => {
+ set($a);
+ setTimeout(() => update((x) => x + 1), 1000);
+ // every time $a produces a value, this produces two
+ // values, $a immediately and then $a + 1 a second later
+});
+```
+
+If you return a function from the callback, it will be called when a) the callback runs again, or b) the last subscriber unsubscribes.
+
+```ts
+// @filename: ambient.d.ts
+import { type Writable } from 'svelte/store';
+
+declare global {
+ const frequency: Writable;
+}
+
+export {};
+
+// @filename: index.ts
+// ---cut---
+import { derived } from 'svelte/store';
+
+const tick = derived(
+ frequency,
+ ($frequency, set) => {
+ const interval = setInterval(() => {
+ set(Date.now());
+ }, 1000 / $frequency);
+
+ return () => {
+ clearInterval(interval);
+ };
+ },
+ 2000
+);
+```
+
+In both cases, an array of arguments can be passed as the first argument instead of a single store.
+
+```ts
+// @filename: ambient.d.ts
+import { type Writable } from 'svelte/store';
+
+declare global {
+ const a: Writable;
+ const b: Writable;
+}
+
+export {};
+
+// @filename: index.ts
+
+// ---cut---
+import { derived } from 'svelte/store';
+
+const summed = derived([a, b], ([$a, $b]) => $a + $b);
+
+const delayed = derived([a, b], ([$a, $b], set) => {
+ setTimeout(() => set($a + $b), 1000);
+});
+```
+
+### `readonly`
+
+This simple helper function makes a store readonly. You can still subscribe to the changes from the original one using this new readable store.
+
+```js
+import { readonly, writable } from 'svelte/store';
+
+const writableStore = writable(1);
+const readableStore = readonly(writableStore);
+
+readableStore.subscribe(console.log);
+
+writableStore.set(2); // console: 2
+// @errors: 2339
+readableStore.set(2); // ERROR
+```
+
+### `get`
+
+Generally, you should read the value of a store by subscribing to it and using the value as it changes over time. Occasionally, you may need to retrieve the value of a store to which you're not subscribed. `get` allows you to do so.
+
+> [!NOTE] This works by creating a subscription, reading the value, then unsubscribing. It's therefore not recommended in hot code paths.
+
+```ts
+// @filename: ambient.d.ts
+import { type Writable } from 'svelte/store';
+
+declare global {
+ const store: Writable;
+}
+
+export {};
+
+// @filename: index.ts
+// ---cut---
+import { get } from 'svelte/store';
+
+const value = get(store);
+```
+
+## Store contract
+
+```ts
+// @noErrors
+store = { subscribe: (subscription: (value: any) => void) => (() => void), set?: (value: any) => void }
+```
+
+You can create your own stores without relying on [`svelte/store`](../svelte-store), by implementing the _store contract_:
+
+1. A store must contain a `.subscribe` method, which must accept as its argument a subscription function. This subscription function must be immediately and synchronously called with the store's current value upon calling `.subscribe`. All of a store's active subscription functions must later be synchronously called whenever the store's value changes.
+2. The `.subscribe` method must return an unsubscribe function. Calling an unsubscribe function must stop its subscription, and its corresponding subscription function must not be called again by the store.
+3. A store may _optionally_ contain a `.set` method, which must accept as its argument a new value for the store, and which synchronously calls all of the store's active subscription functions. Such a store is called a _writable store_.
+
+For interoperability with RxJS Observables, the `.subscribe` method is also allowed to return an object with an `.unsubscribe` method, rather than return the unsubscription function directly. Note however that unless `.subscribe` synchronously calls the subscription (which is not required by the Observable spec), Svelte will see the value of the store as `undefined` until it does.
+
+# Context
+
+Context allows components to access values owned by parent components without passing them down as props (potentially through many layers of intermediate components, known as 'prop-drilling'). The parent component sets context with `setContext(key, value)`...
+
+```svelte
+
+
+```
+
+...and the child retrieves it with `getContext`:
+
+```svelte
+
+
+
+
{message}, inside Child.svelte
+```
+
+This is particularly useful when `Parent.svelte` is not directly aware of `Child.svelte`, but instead renders it as part of a `children` [snippet](snippet) ([demo](/playground/untitled#H4sIAAAAAAAAE42Q3W6DMAyFX8WyJgESK-oto6hTX2D3YxcM3IIUQpR40yqUd58CrCXsp7tL7HNsf2dAWXaEKR56yfTBGOOxFWQwfR6Qz8q1XAHjL-GjUhvzToJd7bU09FO9ctMkG0wxM5VuFeeFLLjtVK8ZnkpNkuGo-w6CTTJ9Z3PwsBAemlbUF934W8iy5DpaZtOUcU02-ZLcaS51jHEkTFm_kY1_wfOO8QnXrb8hBzDEc6pgZ4gFoyz4KgiD7nxfTe8ghqAhIfrJ46cTzVZBbkPlODVJsLCDO6V7ZcJoncyw1yRr0hd1GNn_ZbEM3I9i1bmVxOlWElUvDUNHxpQngt3C4CXzjS1rtvkw22wMrTRtTbC8Lkuabe7jvthPPe3DofYCAAA=)):
+
+```svelte
+
+
+
+```
+
+The key (`'my-context'`, in the example above) and the context itself can be any JavaScript value.
+
diff --git a/.claude/skills/svelte-skill/resources/advanced-concepts-2.md b/.claude/skills/svelte-skill/resources/advanced-concepts-2.md
new file mode 100644
index 0000000..85e5b71
--- /dev/null
+++ b/.claude/skills/svelte-skill/resources/advanced-concepts-2.md
@@ -0,0 +1,338 @@
+In addition to [`setContext`](svelte#setContext) and [`getContext`](svelte#getContext), Svelte exposes [`hasContext`](svelte#hasContext) and [`getAllContexts`](svelte#getAllContexts) functions.
+
+## Using context with state
+
+You can store reactive state in context ([demo](/playground/untitled#H4sIAAAAAAAAE41R0W6DMAz8FSuaBNUQdK8MkKZ-wh7HHihzu6hgosRMm1D-fUpSVNq12x4iEvvOx_kmQU2PIhfP3DCCJGgHYvxkkYid7NCI_GUS_KUcxhVEMjOelErNB3bsatvG4LW6n0ZsRC4K02qpuKqpZtmrQTNMYJA3QRAs7PTQQxS40eMCt3mX3duxnWb-lS5h7nTI0A4jMWoo4c44P_Hku-zrOazdy64chWo-ScfRkRgl8wgHKrLTH1OxHZkHgoHaTraHcopXUFYzPPVfuC_hwQaD1GrskdiNCdQwJljJqlvXfyqVsA5CGg0uRUQifHw56xFtciO75QrP07vo_JXf_tf8yK2ezDKY_ZWt_1y2qqYzv7bI1IW1V_sN19m-07wCAAA=))...
+
+```svelte
+
+
+
+
+
+
+
+```
+
+...though note that if you _reassign_ `counter` instead of updating it, you will 'break the link' — in other words instead of this...
+
+```svelte
+
+```
+
+...you must do this:
+
+```svelte
+
+```
+
+Svelte will warn you if you get it wrong.
+
+## Type-safe context
+
+As an alternative to using `setContext` and `getContext` directly, you can use them via `createContext`. This gives you type safety and makes it unnecessary to use a key:
+
+```ts
+/// file: context.ts
+// @filename: ambient.d.ts
+interface User {}
+
+// @filename: index.ts
+// ---cut---
+import { createContext } from 'svelte';
+
+export const [getUserContext, setUserContext] = createContext();
+```
+
+## Replacing global state
+
+When you have state shared by many different components, you might be tempted to put it in its own module and just import it wherever it's needed:
+
+```js
+/// file: state.svelte.js
+export const myGlobalState = $state({
+ user: {
+ // ...
+ }
+ // ...
+});
+```
+
+In many cases this is perfectly fine, but there is a risk: if you mutate the state during server-side rendering (which is discouraged, but entirely possible!)...
+
+```svelte
+
+
+```
+
+...then the data may be accessible by the _next_ user. Context solves this problem because it is not shared between requests.
+
+# Lifecycle hooks
+
+
+
+In Svelte 5, the component lifecycle consists of only two parts: Its creation and its destruction. Everything in-between — when certain state is updated — is not related to the component as a whole; only the parts that need to react to the state change are notified. This is because under the hood the smallest unit of change is actually not a component, it's the (render) effects that the component sets up upon component initialization. Consequently, there's no such thing as a "before update"/"after update" hook.
+
+## `onMount`
+
+The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM. It must be called during the component's initialisation (but doesn't need to live _inside_ the component; it can be called from an external module).
+
+`onMount` does not run inside a component that is rendered on the server.
+
+```svelte
+
+```
+
+If a function is returned from `onMount`, it will be called when the component is unmounted.
+
+```svelte
+
+```
+
+> [!NOTE] This behaviour will only work when the function passed to `onMount` is _synchronous_. `async` functions always return a `Promise`.
+
+## `onDestroy`
+
+Schedules a callback to run immediately before the component is unmounted.
+
+Out of `onMount`, `beforeUpdate`, `afterUpdate` and `onDestroy`, this is the only one that runs inside a server-side component.
+
+```svelte
+
+```
+
+## `tick`
+
+While there's no "after update" hook, you can use `tick` to ensure that the UI is updated before continuing. `tick` returns a promise that resolves once any pending state changes have been applied, or in the next microtask if there are none.
+
+```svelte
+
+```
+
+## Deprecated: `beforeUpdate` / `afterUpdate`
+
+Svelte 4 contained hooks that ran before and after the component as a whole was updated. For backwards compatibility, these hooks were shimmed in Svelte 5 but not available inside components that use runes.
+
+```svelte
+
+```
+
+Instead of `beforeUpdate` use `$effect.pre` and instead of `afterUpdate` use `$effect` instead - these runes offer more granular control and only react to the changes you're actually interested in.
+
+### Chat window example
+
+To implement a chat window that autoscrolls to the bottom when new messages appear (but only if you were _already_ scrolled to the bottom), we need to measure the DOM before we update it.
+
+In Svelte 4, we do this with `beforeUpdate`, but this is a flawed approach — it fires before _every_ update, whether it's relevant or not. In the example below, we need to introduce checks like `updatingMessages` to make sure we don't mess with the scroll position when someone toggles dark mode.
+
+With runes, we can use `$effect.pre`, which behaves the same as `$effect` but runs before the DOM is updated. As long as we explicitly reference `messages` inside the effect body, it will run whenever `messages` changes, but _not_ when `theme` changes.
+
+`beforeUpdate`, and its equally troublesome counterpart `afterUpdate`, are therefore deprecated in Svelte 5.
+
+- [Before](/playground/untitled#H4sIAAAAAAAAE31WXa_bNgz9K6yL1QmWOLlrC-w6H8MeBgwY9tY9NfdBtmlbiywZkpyPBfnvo2zLcZK28AWuRPGI5OGhkEuQc4EmiL9eAskqDOLg97oOZoE9125jDigs0t6oRqfOsjap5rXd7uTO8qpW2sIFEsyVxn_qjFmcAcstar-xPN3DFXKtKgi768IVgQku0ELj3Lgs_kZjWIEGNpAzYXDlHWyJFZI1zJjeh4O5uvl_DY8oUkVeVoFuJKYls-_CGYS25Aboj0EtWNqel0wWoBoLTGZgmdgDS9zW4Uz4NsrswPHoyutN4xInkylstnBxdmIhh8m7xzqmoNE2Wq46n1RJQzEbq4g-JQSl7e-HDx-GdaTy3KD9E3lRWvj5Zu9QX1QN20dj7zyHz8s-1S6lW7Cpz3RnXTcm04hIlfdFuO8p2mQ5-3a06cqjrn559bF_2NHOnRZ5I1PLlXQNyQT-hedMHeUEDyjtdMxsa4n2eIbNhlTwhyRthaOKOmYtniwF6pwt0wXa6MBEg0OibZec27gz_dk3UrZ6hB2LLYoiv521Yd8Gt-foTrfhiCDP0lC9VUUhcDLU49Xe_9943cNvEArHfAjxeBTovvXiNpFynfEDpIIZs9kFbg52QbeNHWZzebz32s7xHco3nJAJl1nshmhz8dYOQJDyZetnbb2gTWe-vEeWlrfpZMavr56ldb29eNt6UXvgwgFbp_WC0tl2RK25rGk6lYz3nUI2lzvBXGHhPZPGWmKUXFNBKqdaW259wl_aHbiqoVIZdpE60Nax6IOujT0LbFFxIVTCxCRR2XloUcYNvSbnGHKBp763jHoj59xiZWJI0Wm0P_m3MSS985xkasn-cFq20xTDy3J5KFcjgUTD69BHdcHIjz431z28IqlxGcPSfdFnrGDZn6gD6lyo45zyHAD-btczf-98nhQxHEvKfeUtOVkSejD3q-9X7JbzjGtsdUxlKdFU8qGsT78uaw848syWMXz85Waq2Gnem4mAn3prweq4q6Y3JEpnqMmnPoFRgmd3ySW0LLRqSKlwYHriCvJvUs2yjMaaoA-XzTXLeGMe45zmhv_XAno3Mj0xF7USuqNvnE9H343QHlq-eAgxpbTPNR9yzUkgLjwSR0NK4wKoxy-jDg-9vy8sUSToakzW-9fX13Em9Q8T6Z26uZhBN36XUYo5q7ggLXBZoub2Ofv7g6GCZfTxe034NCjiudXj7Omla0eTfo7QBPOcYxbE7qG-vl3_B1G-_i_JCAAA)
+- [After](/playground/untitled#H4sIAAAAAAAAE31WXa-jNhD9K7PsdknUQJLurtRLPqo-VKrU1327uQ8GBnBjbGSb5KZR_nvHgMlXtyIS9njO-MyZGZRzUHCBJkhez4FkNQZJ8HvTBLPAnhq3MQcUFmlvVKszZ1mbTPPGbndyZ3ndKG3hDJZne7hAoVUNYY8JV-RBPgIt2AprhA18MpZZnIQ50_twuvLHNRrDSjRXj9fwiCJTBLIKdCsxq5j9EM4gtBU3QD8GjWBZd14xWYJqLTCZg2ViDyx1W4cz4dv0hsiB49FRHkyfsCgws3GjcTKZwmYLZ2feWc9o1W8zJQ2Fb62i5JUQRNRHgs-fx3WsisKg_RN5WVn4-WrvUd9VA9tH4-AcwbfFQIpkLWByvWzqSe2sk3kyjUlOec_XPU-3TRaz_75tuvKoi19e3OvipSpamVmupJM2F_gXnnJ1lBM8oLQjHceys8R7PMFms4HwD2lRhzeEe-EsvluSrHe2TJdo4wMTLY48XKwPzm0KGm2r5ajFtRYU4TWOY7-ddWHfxhDP0QkQhnf5PWRnVVkKnIx8fZsOb5dR16nwG4TCCRdCMphWQ7z1_DoOcp3zA2SCGbPZBa5jd0G_TRxmc36Me-mG6A7l60XIlMs8ce2-OXtrDyBItdz6qVjPadObzx-RZdV1nJjx64tXad1sz962njceOHfAzmk9JzrbXqg1lw3NkZL7vgE257t-uMDcO6attSSokpmgFqVMO2U93e_dDlzOUKsc-3t6zNZp6K9cG3sS2KGSUqiUiUmq8tNYoJwbmvpTAoXA96GyjCojI26xNglk6DpwOPm7NdRYp4ia0JL94bTqRiGB5WJxqFY37RGPoz3c6i4jP3rcUA7wmhqNywQW7om_YQ2L4UQdUBdCHSPiOQJ8bFcxHzeK0jKBY0XcV95SkCWlD9t-9eOM3TLKucauiyktJdpaPqT19ddF4wFHntsqgS-_XE01e48GMwnw02AtWZP02QyGVOkcNfk072CU4PkduZSWpVYt9SkcmJ64hPwHpWF5ziVls3wIFmmW89Y83vMeGf5PBxjcyPSkXNy10J18t3x6-a6CDtBq6SGklNKeazFyLahB3PVIGo2UbhOgGi9vKjzW_j6xVFFD17difXx5ebll0vwvkcGpn4sZ9MN3vqFYsJoL6gUuK9TcPrO_PxgzWMRfflSEr2NHPJf6lj1957rRpH8CNMG84JgHidUtXt4u_wK21LXERAgAAA==)
+
+
+```svelte
+
+
+
+
+ {#each messages as message}
+
{message}
+ {/each}
+
+
+
+
+
+
+```
+
+# Imperative component API
+
+
+
+Every Svelte application starts by imperatively creating a root component. On the client this component is mounted to a specific element. On the server, you want to get back a string of HTML instead which you can render. The following functions help you achieve those tasks.
+
+## `mount`
+
+Instantiates a component and mounts it to the given target:
+
+```js
+// @errors: 2322
+import { mount } from 'svelte';
+import App from './App.svelte';
+
+const app = mount(App, {
+ target: document.querySelector('#app'),
+ props: { some: 'property' }
+});
+```
+
+You can mount multiple components per page, and you can also mount from within your application, for example when creating a tooltip component and attaching it to the hovered element.
+
+Note that unlike calling `new App(...)` in Svelte 4, things like effects (including `onMount` callbacks, and action functions) will not run during `mount`. If you need to force pending effects to run (in the context of a test, for example) you can do so with `flushSync()`.
+
+## `unmount`
+
+Unmounts a component that was previously created with [`mount`](#mount) or [`hydrate`](#hydrate).
+
+If `options.outro` is `true`, [transitions](transition) will play before the component is removed from the DOM:
+
+```js
+import { mount, unmount } from 'svelte';
+import App from './App.svelte';
+
+const app = mount(App, { target: document.body });
+
+// later
+unmount(app, { outro: true });
+```
+
+Returns a `Promise` that resolves after transitions have completed if `options.outro` is true, or immediately otherwise.
+
+## `render`
+
+Only available on the server and when compiling with the `server` option. Takes a component and returns an object with `body` and `head` properties on it, which you can use to populate the HTML when server-rendering your app:
+
+```js
+// @errors: 2724 2305 2307
+import { render } from 'svelte/server';
+import App from './App.svelte';
+
+const result = render(App, {
+ props: { some: 'property' }
+});
+result.body; // HTML for somewhere in this tag
+result.head; // HTML for somewhere in this tag
+```
+
+## `hydrate`
+
+Like `mount`, but will reuse up any HTML rendered by Svelte's SSR output (from the [`render`](#render) function) inside the target and make it interactive:
+
+```js
+// @errors: 2322
+import { hydrate } from 'svelte';
+import App from './App.svelte';
+
+const app = hydrate(App, {
+ target: document.querySelector('#app'),
+ props: { some: 'property' }
+});
+```
+
+As with `mount`, effects will not run during `hydrate` — use `flushSync()` immediately afterwards if you need them to.
+
diff --git a/.claude/skills/svelte-skill/resources/compiler-errors-1.md b/.claude/skills/svelte-skill/resources/compiler-errors-1.md
new file mode 100644
index 0000000..0ea1a3c
--- /dev/null
+++ b/.claude/skills/svelte-skill/resources/compiler-errors-1.md
@@ -0,0 +1,502 @@
+# Compiler errors
+
+
+
+### animation_duplicate
+
+```
+An element can only have one 'animate' directive
+```
+
+### animation_invalid_placement
+
+```
+An element that uses the `animate:` directive must be the only child of a keyed `{#each ...}` block
+```
+
+### animation_missing_key
+
+```
+An element that uses the `animate:` directive must be the only child of a keyed `{#each ...}` block. Did you forget to add a key to your each block?
+```
+
+### attribute_contenteditable_dynamic
+
+```
+'contenteditable' attribute cannot be dynamic if element uses two-way binding
+```
+
+### attribute_contenteditable_missing
+
+```
+'contenteditable' attribute is required for textContent, innerHTML and innerText two-way bindings
+```
+
+### attribute_duplicate
+
+```
+Attributes need to be unique
+```
+
+### attribute_empty_shorthand
+
+```
+Attribute shorthand cannot be empty
+```
+
+### attribute_invalid_event_handler
+
+```
+Event attribute must be a JavaScript expression, not a string
+```
+
+### attribute_invalid_multiple
+
+```
+'multiple' attribute must be static if select uses two-way binding
+```
+
+### attribute_invalid_name
+
+```
+'%name%' is not a valid attribute name
+```
+
+### attribute_invalid_sequence_expression
+
+```
+Sequence expressions are not allowed as attribute/directive values in runes mode, unless wrapped in parentheses
+```
+
+### attribute_invalid_type
+
+```
+'type' attribute must be a static text value if input uses two-way binding
+```
+
+### attribute_unquoted_sequence
+
+```
+Attribute values containing `{...}` must be enclosed in quote marks, unless the value only contains the expression
+```
+
+### bind_group_invalid_expression
+
+```
+`bind:group` can only bind to an Identifier or MemberExpression
+```
+
+### bind_group_invalid_snippet_parameter
+
+```
+Cannot `bind:group` to a snippet parameter
+```
+
+### bind_invalid_expression
+
+```
+Can only bind to an Identifier or MemberExpression or a `{get, set}` pair
+```
+
+### bind_invalid_name
+
+```
+`bind:%name%` is not a valid binding
+```
+
+```
+`bind:%name%` is not a valid binding. %explanation%
+```
+
+### bind_invalid_parens
+
+```
+`bind:%name%={get, set}` must not have surrounding parentheses
+```
+
+### bind_invalid_target
+
+```
+`bind:%name%` can only be used with %elements%
+```
+
+### bind_invalid_value
+
+```
+Can only bind to state or props
+```
+
+### bindable_invalid_location
+
+```
+`$bindable()` can only be used inside a `$props()` declaration
+```
+
+### block_duplicate_clause
+
+```
+%name% cannot appear more than once within a block
+```
+
+### block_invalid_continuation_placement
+
+```
+{:...} block is invalid at this position (did you forget to close the preceding element or block?)
+```
+
+### block_invalid_elseif
+
+```
+'elseif' should be 'else if'
+```
+
+### block_invalid_placement
+
+```
+{#%name% ...} block cannot be %location%
+```
+
+### block_unclosed
+
+```
+Block was left open
+```
+
+### block_unexpected_character
+
+```
+Expected a `%character%` character immediately following the opening bracket
+```
+
+### block_unexpected_close
+
+```
+Unexpected block closing tag
+```
+
+### component_invalid_directive
+
+```
+This type of directive is not valid on components
+```
+
+### const_tag_cycle
+
+```
+Cyclical dependency detected: %cycle%
+```
+
+### const_tag_invalid_expression
+
+```
+{@const ...} must consist of a single variable declaration
+```
+
+### const_tag_invalid_placement
+
+```
+`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, ``, ``
+```
+
+### const_tag_invalid_reference
+
+```
+The `{@const %name% = ...}` declaration is not available in this snippet
+```
+
+The following is an error:
+
+```svelte
+
+ {@const foo = 'bar'}
+
+ {#snippet failed()}
+ {foo}
+ {/snippet}
+
+```
+
+Here, `foo` is not available inside `failed`. The top level code inside `` becomes part of the implicit `children` snippet, in other words the above code is equivalent to this:
+
+```svelte
+
+ {#snippet children()}
+ {@const foo = 'bar'}
+ {/snippet}
+
+ {#snippet failed()}
+ {foo}
+ {/snippet}
+
+```
+
+The same applies to components:
+
+```svelte
+
+ {@const foo = 'bar'}
+
+ {#snippet someProp()}
+
+ {foo}
+ {/snippet}
+
+```
+
+### constant_assignment
+
+```
+Cannot assign to %thing%
+```
+
+### constant_binding
+
+```
+Cannot bind to %thing%
+```
+
+### css_empty_declaration
+
+```
+Declaration cannot be empty
+```
+
+### css_expected_identifier
+
+```
+Expected a valid CSS identifier
+```
+
+### css_global_block_invalid_combinator
+
+```
+A `:global` selector cannot follow a `%name%` combinator
+```
+
+### css_global_block_invalid_declaration
+
+```
+A top-level `:global {...}` block can only contain rules, not declarations
+```
+
+### css_global_block_invalid_list
+
+```
+A `:global` selector cannot be part of a selector list with entries that don't contain `:global`
+```
+
+The following CSS is invalid:
+
+```css
+:global, x {
+ y {
+ color: red;
+ }
+}
+```
+
+This is mixing a `:global` block, which means "everything in here is unscoped", with a scoped selector (`x` in this case). As a result it's not possible to transform the inner selector (`y` in this case) into something that satisfies both requirements. You therefore have to split this up into two selectors:
+
+```css
+:global {
+ y {
+ color: red;
+ }
+}
+
+x y {
+ color: red;
+}
+```
+
+### css_global_block_invalid_modifier
+
+```
+A `:global` selector cannot modify an existing selector
+```
+
+### css_global_block_invalid_modifier_start
+
+```
+A `:global` selector can only be modified if it is a descendant of other selectors
+```
+
+### css_global_block_invalid_placement
+
+```
+A `:global` selector cannot be inside a pseudoclass
+```
+
+### css_global_invalid_placement
+
+```
+`:global(...)` can be at the start or end of a selector sequence, but not in the middle
+```
+
+### css_global_invalid_selector
+
+```
+`:global(...)` must contain exactly one selector
+```
+
+### css_global_invalid_selector_list
+
+```
+`:global(...)` must not contain type or universal selectors when used in a compound selector
+```
+
+### css_nesting_selector_invalid_placement
+
+```
+Nesting selectors can only be used inside a rule or as the first selector inside a lone `:global(...)`
+```
+
+### css_selector_invalid
+
+```
+Invalid selector
+```
+
+### css_type_selector_invalid_placement
+
+```
+`:global(...)` must not be followed by a type selector
+```
+
+### debug_tag_invalid_arguments
+
+```
+{@debug ...} arguments must be identifiers, not arbitrary expressions
+```
+
+### declaration_duplicate
+
+```
+`%name%` has already been declared
+```
+
+### declaration_duplicate_module_import
+
+```
+Cannot declare a variable with the same name as an import inside `
+
+{#each array as entry}
+
+
+
+
+
+{/each}
+```
+
+This turned out to be buggy and unpredictable, particularly when working with derived values (such as `array.map(...)`), and as such is forbidden in runes mode. You can achieve the same outcome by using the index instead:
+
+```svelte
+
+
+{#each array as entry, i}
+
+
+
+
+
+{/each}
+```
+
+### each_key_without_as
+
+```
+An `{#each ...}` block without an `as` clause cannot have a key
+```
+
+### effect_invalid_placement
+
+```
+`$effect()` can only be used as an expression statement
+```
+
+### element_invalid_closing_tag
+
+```
+`%name%>` attempted to close an element that was not open
+```
+
+### element_invalid_closing_tag_autoclosed
+
+```
+`%name%>` attempted to close element that was already automatically closed by `<%reason%>` (cannot nest `<%reason%>` inside `<%name%>`)
+```
+
+### element_unclosed
+
+```
+`<%name%>` was left open
+```
+
+### event_handler_invalid_component_modifier
+
+```
+Event modifiers other than 'once' can only be used on DOM elements
+```
+
+### event_handler_invalid_modifier
+
+```
+Valid event modifiers are %list%
+```
+
+### event_handler_invalid_modifier_combination
+
+```
diff --git a/.claude/skills/svelte-skill/resources/compiler-errors-2.md b/.claude/skills/svelte-skill/resources/compiler-errors-2.md
new file mode 100644
index 0000000..b3f1c86
--- /dev/null
+++ b/.claude/skills/svelte-skill/resources/compiler-errors-2.md
@@ -0,0 +1,350 @@
+The '%modifier1%' and '%modifier2%' modifiers cannot be used together
+```
+
+### expected_attribute_value
+
+```
+Expected attribute value
+```
+
+### expected_block_type
+
+```
+Expected 'if', 'each', 'await', 'key' or 'snippet'
+```
+
+### expected_identifier
+
+```
+Expected an identifier
+```
+
+### expected_pattern
+
+```
+Expected identifier or destructure pattern
+```
+
+### expected_token
+
+```
+Expected token %token%
+```
+
+### expected_whitespace
+
+```
+Expected whitespace
+```
+
+### experimental_async
+
+```
+Cannot use `await` in deriveds and template expressions, or at the top level of a component, unless the `experimental.async` compiler option is `true`
+```
+
+### export_undefined
+
+```
+`%name%` is not defined
+```
+
+### global_reference_invalid
+
+```
+`%name%` is an illegal variable name. To reference a global variable called `%name%`, use `globalThis.%name%`
+```
+
+### host_invalid_placement
+
+```
+`$host()` can only be used inside custom element component instances
+```
+
+### illegal_element_attribute
+
+```
+`<%name%>` does not support non-event attributes or spread attributes
+```
+
+### import_svelte_internal_forbidden
+
+```
+Imports of `svelte/internal/*` are forbidden. It contains private runtime code which is subject to change without notice. If you're importing from `svelte/internal/*` to work around a limitation of Svelte, please open an issue at https://github.com/sveltejs/svelte and explain your use case
+```
+
+### inspect_trace_generator
+
+```
+`$inspect.trace(...)` cannot be used inside a generator function
+```
+
+### inspect_trace_invalid_placement
+
+```
+`$inspect.trace(...)` must be the first statement of a function body
+```
+
+### invalid_arguments_usage
+
+```
+The arguments keyword cannot be used within the template or at the top level of a component
+```
+
+### js_parse_error
+
+```
+%message%
+```
+
+### legacy_await_invalid
+
+```
+Cannot use `await` in deriveds and template expressions, or at the top level of a component, unless in runes mode
+```
+
+### legacy_export_invalid
+
+```
+Cannot use `export let` in runes mode — use `$props()` instead
+```
+
+### legacy_props_invalid
+
+```
+Cannot use `$$props` in runes mode
+```
+
+### legacy_reactive_statement_invalid
+
+```
+`$:` is not allowed in runes mode, use `$derived` or `$effect` instead
+```
+
+### legacy_rest_props_invalid
+
+```
+Cannot use `$$restProps` in runes mode
+```
+
+### let_directive_invalid_placement
+
+```
+`let:` directive at invalid position
+```
+
+### mixed_event_handler_syntaxes
+
+```
+Mixing old (on:%name%) and new syntaxes for event handling is not allowed. Use only the on%name% syntax
+```
+
+### module_illegal_default_export
+
+```
+A component cannot have a default export
+```
+
+### node_invalid_placement
+
+```
+%message%. The browser will 'repair' the HTML (by moving, removing, or inserting elements) which breaks Svelte's assumptions about the structure of your components.
+```
+
+HTML restricts where certain elements can appear. In case of a violation the browser will 'repair' the HTML in a way that breaks Svelte's assumptions about the structure of your components. Some examples:
+
+- `
hello
world
` will result in `
hello
world
` (the `
` autoclosed the `
` because `
` cannot contain block-level elements)
+- `` will result in `` (the `
` is removed)
+- `
cell
` will result in `
cell
` (a `` is auto-inserted)
+
+### options_invalid_value
+
+```
+Invalid compiler option: %details%
+```
+
+### options_removed
+
+```
+Invalid compiler option: %details%
+```
+
+### options_unrecognised
+
+```
+Unrecognised compiler option %keypath%
+```
+
+### props_duplicate
+
+```
+Cannot use `%rune%()` more than once
+```
+
+### props_id_invalid_placement
+
+```
+`$props.id()` can only be used at the top level of components as a variable declaration initializer
+```
+
+### props_illegal_name
+
+```
+Declaring or accessing a prop starting with `$$` is illegal (they are reserved for Svelte internals)
+```
+
+### props_invalid_identifier
+
+```
+`$props()` can only be used with an object destructuring pattern
+```
+
+### props_invalid_pattern
+
+```
+`$props()` assignment must not contain nested properties or computed keys
+```
+
+### props_invalid_placement
+
+```
+`$props()` can only be used at the top level of components as a variable declaration initializer
+```
+
+### reactive_declaration_cycle
+
+```
+Cyclical dependency detected: %cycle%
+```
+
+### render_tag_invalid_call_expression
+
+```
+Calling a snippet function using apply, bind or call is not allowed
+```
+
+### render_tag_invalid_expression
+
+```
+`{@render ...}` tags can only contain call expressions
+```
+
+### render_tag_invalid_spread_argument
+
+```
+cannot use spread arguments in `{@render ...}` tags
+```
+
+### rune_invalid_arguments
+
+```
+`%rune%` cannot be called with arguments
+```
+
+### rune_invalid_arguments_length
+
+```
+`%rune%` must be called with %args%
+```
+
+### rune_invalid_computed_property
+
+```
+Cannot access a computed property of a rune
+```
+
+### rune_invalid_name
+
+```
+`%name%` is not a valid rune
+```
+
+### rune_invalid_spread
+
+```
+`%rune%` cannot be called with a spread argument
+```
+
+### rune_invalid_usage
+
+```
+Cannot use `%rune%` rune in non-runes mode
+```
+
+### rune_missing_parentheses
+
+```
+Cannot use rune without parentheses
+```
+
+### rune_removed
+
+```
+The `%name%` rune has been removed
+```
+
+### rune_renamed
+
+```
+`%name%` is now `%replacement%`
+```
+
+### runes_mode_invalid_import
+
+```
+%name% cannot be used in runes mode
+```
+
+### script_duplicate
+
+```
+A component can have a single top-level `
+
+
This value updates: {reactive}
+
This value does not update: {stale}
+
+
+```
+
+To fix this, wrap your variable declaration with `$state`.
+
+### options_deprecated_accessors
+
+```
+The `accessors` option has been deprecated. It will have no effect in runes mode
+```
+
+### options_deprecated_immutable
+
+```
+The `immutable` option has been deprecated. It will have no effect in runes mode
+```
+
+### options_missing_custom_element
+
+```
+The `customElement` option is used when generating a custom element. Did you forget the `customElement: true` compile option?
+```
+
+### options_removed_enable_sourcemap
+
+```
+The `enableSourcemap` option has been removed. Source maps are always generated now, and tooling can choose to ignore them
+```
+
+### options_removed_hydratable
+
+```
+The `hydratable` option has been removed. Svelte components are always hydratable now
+```
+
+### options_removed_loop_guard_timeout
+
+```
+The `loopGuardTimeout` option has been removed
+```
+
+### options_renamed_ssr_dom
+
+```
+`generate: "dom"` and `generate: "ssr"` options have been renamed to "client" and "server" respectively
+```
+
+### perf_avoid_inline_class
+
+```
+Avoid 'new class' — instead, declare the class at the top level scope
+```
+
+### perf_avoid_nested_class
+
+```
+Avoid declaring classes below the top level scope
+```
+
+### reactive_declaration_invalid_placement
+
+```
+Reactive declarations only exist at the top level of the instance script
+```
+
+### reactive_declaration_module_script_dependency
+
+```
+Reassignments of module-level declarations will not cause reactive statements to update
+```
+
+### script_context_deprecated
+
+```
+`context="module"` is deprecated, use the `module` attribute instead
+```
+
+```svelte
+
+```
+
+### script_unknown_attribute
+
+```
+Unrecognized attribute — should be one of `generics`, `lang` or `module`. If this exists for a preprocessor, ensure that the preprocessor removes it
+```
+
+### slot_element_deprecated
+
+```
+Using `` to render parent content is deprecated. Use `{@render ...}` tags instead
+```
+
+See [the migration guide](v5-migration-guide#Snippets-instead-of-slots) for more info.
+
+### state_referenced_locally
+
+```
+This reference only captures the initial value of `%name%`. Did you mean to reference it inside a %type% instead?
+```
+
+This warning is thrown when the compiler detects the following:
+
+- A reactive variable is declared
+- ...and later reassigned...
+- ...and referenced in the same scope
+
+This 'breaks the link' to the original state declaration. For example, if you pass the state to a function, the function loses access to the state once it is reassigned:
+
+```svelte
+
+
+
+
+```
+
+```svelte
+
+
+
+
+
The count is {count}
+```
+
+To fix this, reference the variable such that it is lazily evaluated. For the above example, this can be achieved by wrapping `count` in a function:
+
+```svelte
+
+
+
+
+```
+
+```svelte
+
+
+
+
+
The count is {+++count()+++}
+```
+
+For more info, see [Passing state into functions]($state#Passing-state-into-functions).
+
+### store_rune_conflict
+
+```
+It looks like you're using the `$%name%` rune, but there is a local binding called `%name%`. Referencing a local variable with a `$` prefix will create a store subscription. Please rename `%name%` to avoid the ambiguity
+```
+
+### svelte_component_deprecated
+
+```
+`` is deprecated in runes mode — components are dynamic by default
+```
+
+In previous versions of Svelte, the component constructor was fixed when the component was rendered. In other words, if you wanted `` to re-render when `X` changed, you would either have to use `` or put the component inside a `{#key X}...{/key}` block.
+
+In Svelte 5 this is no longer true — if `X` changes, `` re-renders.
+
+In some cases `` syntax can be used as a replacement; a lowercased variable with property access is recognized as a component in Svelte 5.
+
+For complex component resolution logic, an intermediary, capitalized variable may be necessary. E.g. in places where `@const` can be used:
+
+
+```svelte
+{#each items as item}
+ ------
+ +++{@const Component = item.condition ? Y : Z}+++
+ ++++++
+{/each}
+```
+
+A derived value may be used in other contexts:
+
+
+```svelte
+
+
+------
+++++++
+```
+
+### svelte_element_invalid_this
+
+```
+`this` should be an `{expression}`. Using a string attribute value will cause an error in future versions of Svelte
+```
+
+### svelte_self_deprecated
+
+```
+`` is deprecated — use self-imports (e.g. `import %name% from './%basename%'`) instead
+```
+
+See [the note in the docs](legacy-svelte-self) for more info.
+
+### unknown_code
+
+```
+`%code%` is not a recognised code
+```
+
+```
+`%code%` is not a recognised code (did you mean `%suggestion%`?)
+```
+
diff --git a/.claude/skills/svelte-skill/resources/components.md b/.claude/skills/svelte-skill/resources/components.md
new file mode 100644
index 0000000..8f16114
--- /dev/null
+++ b/.claude/skills/svelte-skill/resources/components.md
@@ -0,0 +1,79 @@
+# .svelte files
+
+Components are the building blocks of Svelte applications. They are written into `.svelte` files, using a superset of HTML.
+
+All three sections — script, styles and markup — are optional.
+
+
+```svelte
+/// file: MyComponent.svelte
+
+
+
+
+
+
+
+```
+
+## `
+
+
+```
+
+You can `export` bindings from this block, and they will become exports of the compiled module. You cannot `export default`, since the default export is the component itself.
+
+> [!NOTE] If you are using TypeScript and import such exports from a `module` block into a `.ts` file, make sure to have your editor setup so that TypeScript knows about them. This is the case for our VS Code extension and the IntelliJ plugin, but in other cases you might need to setup our [TypeScript editor plugin](https://www.npmjs.com/package/typescript-svelte-plugin).
+
+> [!LEGACY]
+> In Svelte 4, this script tag was created using `
+
+
+
+{#if visible}
+
fades in and out
+{/if}
+```
+
+## Local vs global
+
+Transitions are local by default. Local transitions only play when the block they belong to is created or destroyed, _not_ when parent blocks are created or destroyed.
+
+```svelte
+{#if x}
+ {#if y}
+
fades in and out only when y changes
+
+
fades in and out when x or y change
+ {/if}
+{/if}
+```
+
+## Built-in transitions
+
+A selection of built-in transitions can be imported from the [`svelte/transition`](svelte-transition) module.
+
+## Transition parameters
+
+Transitions can have parameters.
+
+(The double `{{curlies}}` aren't a special syntax; this is an object literal inside an expression tag.)
+
+```svelte
+{#if visible}
+
fades in and out over two seconds
+{/if}
+```
+
+## Custom transition functions
+
+```js
+/// copy: false
+// @noErrors
+transition = (node: HTMLElement, params: any, options: { direction: 'in' | 'out' | 'both' }) => {
+ delay?: number,
+ duration?: number,
+ easing?: (t: number) => number,
+ css?: (t: number, u: number) => string,
+ tick?: (t: number, u: number) => void
+}
+```
+
+Transitions can use custom functions. If the returned object has a `css` function, Svelte will generate keyframes for a [web animation](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API).
+
+The `t` argument passed to `css` is a value between `0` and `1` after the `easing` function has been applied. _In_ transitions run from `0` to `1`, _out_ transitions run from `1` to `0` — in other words, `1` is the element's natural state, as though no transition had been applied. The `u` argument is equal to `1 - t`.
+
+The function is called repeatedly _before_ the transition begins, with different `t` and `u` arguments.
+
+```svelte
+
+
+
+{#if visible}
+
whooshes in
+{/if}
+```
+
+A custom transition function can also return a `tick` function, which is called _during_ the transition with the same `t` and `u` arguments.
+
+> [!NOTE] If it's possible to use `css` instead of `tick`, do so — web animations can run off the main thread, preventing jank on slower devices.
+
+```svelte
+
+
+
+{#if visible}
+
The quick brown fox jumps over the lazy dog
+{/if}
+```
+
+If a transition returns a function instead of a transition object, the function will be called in the next microtask. This allows multiple transitions to coordinate, making [crossfade effects](/tutorial/deferred-transitions) possible.
+
+Transition functions also receive a third argument, `options`, which contains information about the transition.
+
+Available values in the `options` object are:
+
+- `direction` - one of `in`, `out`, or `both` depending on the type of transition
+
+## Transition events
+
+An element with transitions will dispatch the following events in addition to any standard DOM events:
+
+- `introstart`
+- `introend`
+- `outrostart`
+- `outroend`
+
+```svelte
+{#if visible}
+
+{/if}
+```
+
+# in: and out:
+
+The `in:` and `out:` directives are identical to [`transition:`](transition), except that the resulting transitions are not bidirectional — an `in` transition will continue to 'play' alongside the `out` transition, rather than reversing, if the block is outroed while the transition is in progress. If an out transition is aborted, transitions will restart from scratch.
+
+```svelte
+
+
+
+
+{#if visible}
+
flies in, fades out
+{/if}
+```
+
+# animate:
+
+An animation is triggered when the contents of a [keyed each block](each#Keyed-each-blocks) are re-ordered. Animations do not run when an element is added or removed, only when the index of an existing data item within the each block changes. Animate directives must be on an element that is an _immediate_ child of a keyed each block.
+
+Animations can be used with Svelte's [built-in animation functions](svelte-animate) or [custom animation functions](#Custom-animation-functions).
+
+```svelte
+
+{#each list as item, index (item)}
+
{item}
+{/each}
+```
+
+## Animation Parameters
+
+As with actions and transitions, animations can have parameters.
+
+(The double `{{curlies}}` aren't a special syntax; this is an object literal inside an expression tag.)
+
+```svelte
+{#each list as item, index (item)}
+
{item}
+{/each}
+```
+
+## Custom animation functions
+
+```js
+/// copy: false
+// @noErrors
+animation = (node: HTMLElement, { from: DOMRect, to: DOMRect } , params: any) => {
+ delay?: number,
+ duration?: number,
+ easing?: (t: number) => number,
+ css?: (t: number, u: number) => string,
+ tick?: (t: number, u: number) => void
+}
+```
+
+Animations can use custom functions that provide the `node`, an `animation` object and any `parameters` as arguments. The `animation` parameter is an object containing `from` and `to` properties each containing a [DOMRect](https://developer.mozilla.org/en-US/docs/Web/API/DOMRect#Properties) describing the geometry of the element in its `start` and `end` positions. The `from` property is the DOMRect of the element in its starting position, and the `to` property is the DOMRect of the element in its final position after the list has been reordered and the DOM updated.
+
+If the returned object has a `css` method, Svelte will create a [web animation](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API) that plays on the element.
+
+The `t` argument passed to `css` is a value that goes from `0` and `1` after the `easing` function has been applied. The `u` argument is equal to `1 - t`.
+
+The function is called repeatedly _before_ the animation begins, with different `t` and `u` arguments.
+
+
+
+```svelte
+
+
+
+{#each list as item, index (item)}
+
{item}
+{/each}
+```
+
+A custom animation function can also return a `tick` function, which is called _during_ the animation with the same `t` and `u` arguments.
+
+> [!NOTE] If it's possible to use `css` instead of `tick`, do so — web animations can run off the main thread, preventing jank on slower devices.
+
+```svelte
+
+
+
+{#each list as item, index (item)}
+
{item}
+{/each}
+```
+
+# style:
+
+The `style:` directive provides a shorthand for setting multiple styles on an element.
+
+```svelte
+
+
...
+
...
+```
+
+The value can contain arbitrary expressions:
+
+```svelte
+
...
+```
+
+The shorthand form is allowed:
+
+```svelte
+
...
+```
+
+Multiple styles can be set on a single element:
+
+```svelte
+
...
+```
+
+To mark a style as important, use the `|important` modifier:
+
+```svelte
+
...
+```
+
+When `style:` directives are combined with `style` attributes, the directives will take precedence,
+even over `!important` properties:
+
+```svelte
+
This will be red
+
This will still be red
+```
+
+# class
+
+There are two ways to set classes on elements: the `class` attribute, and the `class:` directive.
+
+## Attributes
+
+Primitive values are treated like any other attribute:
+
+```svelte
+
...
+```
+
+> [!NOTE]
+> For historical reasons, falsy values (like `false` and `NaN`) are stringified (`class="false"`), though `class={undefined}` (or `null`) cause the attribute to be omitted altogether. In a future version of Svelte, all falsy values will cause `class` to be omitted.
+
+### Objects and arrays
+
+Since Svelte 5.16, `class` can be an object or array, and is converted to a string using [clsx](https://github.com/lukeed/clsx).
+
+If the value is an object, the truthy keys are added:
+
+```svelte
+
+
+
+
...
+```
+
+If the value is an array, the truthy values are combined:
+
+```svelte
+
+
...
+```
+
+Note that whether we're using the array or object form, we can set multiple classes simultaneously with a single condition, which is particularly useful if you're using things like Tailwind.
+
+Arrays can contain arrays and objects, and clsx will flatten them. This is useful for combining local classes with props, for example:
+
+```svelte
+
+
+
+
+```
+
+The user of this component has the same flexibility to use a mixture of objects, arrays and strings:
+
+```svelte
+
+
+
+
+```
+
+Since Svelte 5.19, Svelte also exposes the `ClassValue` type, which is the type of value that the `class` attribute on elements accept. This is useful if you want to use a type-safe class name in component props:
+
+```svelte
+
+
+
...
+```
+
+## The `class:` directive
+
+Prior to Svelte 5.16, the `class:` directive was the most convenient way to set classes on elements conditionally.
+
+```svelte
+
+
...
+
...
+```
+
+As with other directives, we can use a shorthand when the name of the class coincides with the value:
+
+```svelte
+
...
+```
+
+> [!NOTE] Unless you're using an older version of Svelte, consider avoiding `class:`, since the attribute is more powerful and composable.
+
+# await
+
+As of Svelte 5.36, you can use the `await` keyword inside your components in three places where it was previously unavailable:
+
+- at the top level of your component's `
+
+
+
+
+
{a} + {b} = {await add(a, b)}
+```
+
+...if you increment `a`, the contents of the `
` will _not_ immediately update to read this —
+
+```html
+
2 + 2 = 3
+```
+
+— instead, the text will update to `2 + 2 = 4` when `add(a, b)` resolves.
+
+Updates can overlap — a fast update will be reflected in the UI while an earlier slow update is still ongoing.
+
+## Concurrency
+
+Svelte will do as much asynchronous work as it can in parallel. For example if you have two `await` expressions in your markup...
+
+```svelte
+
{await one()}
+
{await two()}
+```
+
+...both functions will run at the same time, as they are independent expressions, even though they are _visually_ sequential.
+
+This does not apply to sequential `await` expressions inside your `
+
+
+
+{#if open}
+
+
+```
+
+> [!NOTE]
+> Use reset buttons sparingly, and ensure that users won't accidentally click them while trying to submit the form.
+
+## ``
+
+Checkbox inputs can be bound with `bind:checked`:
+
+```svelte
+
+```
+
+Since 5.6.0, if an `` has a `defaultChecked` attribute and is part of a form, it will revert to that value instead of `false` when the form is reset. Note that for the initial render the value of the binding takes precedence unless it is `null` or `undefined`.
+
+```svelte
+
+
+
+```
+
+> [!NOTE] Use `bind:group` for radio inputs instead of `bind:checked`.
+
+## ``
+
+Checkboxes can be in an [indeterminate](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/indeterminate) state, independently of whether they are checked or unchecked:
+
+```svelte
+
+
+
+```
+
+## ``
+
+Inputs that work together can use `bind:group` ([demo](/playground/untitled#H4sIAAAAAAAAE62T32_TMBDH_5XDQkpbrct7SCMGEvCEECDxsO7BSW6L2c227EvbKOv_jp0f6jYhQKJv5_P3PvdL1wstH1Bk4hMSGdgbRzUssFaM9VJciFtF6EV23QvubNRFR_BPUVfWXvodEkdfKT3-zl8Zzag5YETuK6csF1u9ZUIGNo4VkYQNvPYsGRfJF5JKJ8s3QRJE6WoFb2Nq6K-ck13u2Sl9Vxxhlc6QUBIFnz9Brm9ifJ6esun81XoNd860FmtwslYGlLYte5AO4aHlVhJ1gIeKWq92COt1iMtJlkhFPkgh1rHZiiF6K6BUus4G5KafGznCTlIbVUMfQZUWMJh5OrL-C_qjMYSwb1DyiH7iOEuCb1ZpWTUjfHqcwC_GWDVY3ZfmME_SGttSmD9IHaYatvWHIc6xLyqad3mq6KuqcCwnWn9p8p-p71BqP2IH81zc9w2in-od7XORP7ayCpd5YCeXI_-p59mObPF9WmwGpx3nqS2Gzw8TO3zOaS5_GqUXyQUkS3h8hOSz0ZhMESHGc0c4Hm3MAn00t1wrb0l2GZRkqvt4sXwczm6Qh8vnUJzI2LV4vAkvqWgfehTZrSSPx19WiVfFfAQAAA==)):
+
+```svelte
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+> [!NOTE] `bind:group` only works if the inputs are in the same Svelte component.
+
+## ``
+
+On `` elements with `type="file"`, you can use `bind:files` to get the [`FileList` of selected files](https://developer.mozilla.org/en-US/docs/Web/API/FileList). When you want to update the files programmatically, you always need to use a `FileList` object. Currently `FileList` objects cannot be constructed directly, so you need to create a new [`DataTransfer`](https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer) object and get `files` from there.
+
+```svelte
+
+
+
+
+
+```
+
+`FileList` objects also cannot be modified, so if you want to e.g. delete a single file from the list, you need to create a new `DataTransfer` object and add the files you want to keep.
+
+> [!NOTE] `DataTransfer` may not be available in server-side JS runtimes. Leaving the state that is bound to `files` uninitialized prevents potential errors if components are server-side rendered.
+
+## `