diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 3dfa38c..2d8fe85 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -7,6 +7,14 @@ "description": "Claude Code skills, agents, and configuration — the parts worth sharing.", "pluginRoot": "./plugins" }, + "categories": [ + { "slug": "research-knowledge", "name": "Research & Knowledge", "display_order": 1 }, + { "slug": "code-quality", "name": "Code Quality", "display_order": 2 }, + { "slug": "data-engineering", "name": "Data Engineering", "display_order": 3 }, + { "slug": "documentation", "name": "Documentation", "display_order": 4 }, + { "slug": "devops", "name": "DevOps & Shipping", "display_order": 5 }, + { "slug": "productivity", "name": "Productivity", "display_order": 6 } + ], "plugins": [ { "name": "research", @@ -14,7 +22,9 @@ "description": "Process any source into a structured, compounding knowledge base", "version": "0.3.0", "author": { "name": "harnessprotocol" }, - "license": "Apache-2.0" + "license": "Apache-2.0", + "category": "research-knowledge", + "tags": ["research", "knowledge-base", "web-scraping", "pdf", "github", "sources"] }, { "name": "explain", @@ -22,7 +32,9 @@ "description": "Structured code explainer — layered explanations of files, functions, directories, or concepts", "version": "0.2.0", "author": { "name": "harnessprotocol" }, - "license": "Apache-2.0" + "license": "Apache-2.0", + "category": "code-quality", + "tags": ["code-explanation", "learning", "walkthrough", "codebase-understanding"] }, { "name": "data-lineage", @@ -30,7 +42,9 @@ "description": "Trace column-level data lineage through SQL, Kafka, Spark, and JDBC codebases", "version": "0.2.0", "author": { "name": "harnessprotocol" }, - "license": "Apache-2.0" + "license": "Apache-2.0", + "category": "data-engineering", + "tags": ["sql", "data-lineage", "kafka", "spark", "jdbc", "column-level"] }, { "name": "orient", @@ -38,7 +52,9 @@ "description": "Topic-focused session orientation — search graph, knowledge, journal, and research for a specific topic", "version": "0.2.0", "author": { "name": "harnessprotocol" }, - "license": "Apache-2.0" + "license": "Apache-2.0", + "category": "research-knowledge", + "tags": ["orientation", "knowledge-graph", "session", "topic-search"] }, { "name": "capture-session", @@ -46,7 +62,9 @@ "description": "Capture session information into a staging file for later reflection and knowledge graph processing", "version": "0.2.0", "author": { "name": "harnessprotocol" }, - "license": "Apache-2.0" + "license": "Apache-2.0", + "category": "research-knowledge", + "tags": ["session-capture", "knowledge-graph", "reflection", "staging"] }, { "name": "review", @@ -54,7 +72,9 @@ "description": "Code review for a branch, PR, or path — structured output with severity labels and cross-file analysis", "version": "0.3.0", "author": { "name": "harnessprotocol" }, - "license": "Apache-2.0" + "license": "Apache-2.0", + "category": "code-quality", + "tags": ["code-review", "pull-request", "static-analysis", "github"] }, { "name": "docgen", @@ -62,15 +82,19 @@ "description": "Generate or update README, API docs, architecture overview, or changelog — always confirms before writing", "version": "0.2.0", "author": { "name": "harnessprotocol" }, - "license": "Apache-2.0" + "license": "Apache-2.0", + "category": "documentation", + "tags": ["documentation", "readme", "api-docs", "changelog", "architecture"] }, { "name": "harness-share", "source": "./harness-share", - "description": "Export, share, and import your harness-kit plugin configuration — capture your setup into harness.yaml and restore it anywhere", - "version": "0.2.0", + "description": "Compile, export, import, and sync harness configurations across Claude Code, Cursor, and GitHub Copilot", + "version": "0.3.0", "author": { "name": "harnessprotocol" }, - "license": "Apache-2.0" + "license": "Apache-2.0", + "category": "productivity", + "tags": ["configuration", "export", "import", "yaml", "cross-platform"] }, { "name": "ship-pr", @@ -78,7 +102,9 @@ "description": "End-of-task shipping workflow: run tests, open a PR, code review, fix CI, sync base, then squash merge", "version": "0.1.0", "author": { "name": "harnessprotocol" }, - "license": "Apache-2.0" + "license": "Apache-2.0", + "category": "devops", + "tags": ["pull-request", "ci-cd", "merge", "testing", "workflow"] }, { "name": "pull-request-sweep", @@ -86,7 +112,9 @@ "description": "Cross-repo PR sweep: triage all open PRs, run code reviews, merge what's ready, fix quick CI blockers, and report", "version": "0.1.0", "author": { "name": "harnessprotocol" }, - "license": "Apache-2.0" + "license": "Apache-2.0", + "category": "devops", + "tags": ["pull-request", "triage", "ci-cd", "code-review", "multi-repo"] } ] } diff --git a/.gitignore b/.gitignore index 9e64cfd..beff742 100644 --- a/.gitignore +++ b/.gitignore @@ -9,8 +9,26 @@ venv/ private/ docs/plans/ -# Docusaurus -website/node_modules/ +# Node +node_modules/ + +# Website (Fumadocs) website/build/ website/.docusaurus/ website/.cache-loader/ +website/.next/ +website/out/ +website/.source/ + +# Marketplace +marketplace/.next/ +marketplace/out/ + +# Supabase local +marketplace/supabase/.temp/ + +# Shared package build output +packages/shared/dist/ + +# Playwright MCP +.playwright-mcp/ diff --git a/README.md b/README.md index 63d25d0..a011172 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,11 @@ A configuration framework for AI coding tools. [![Validate](https://img.shields.io/github/actions/workflow/status/harnessprotocol/harness-kit/validate.yml?style=flat-square&label=validate)](https://github.com/harnessprotocol/harness-kit/actions/workflows/validate.yml) [![License](https://img.shields.io/github/license/harnessprotocol/harness-kit?style=flat-square)](LICENSE) -Requires [Claude Code](https://claude.ai/claude-code) +Works with [Claude Code](https://claude.ai/claude-code), [Cursor](https://cursor.com), and [GitHub Copilot](https://github.com/features/copilot) -Building a good AI setup takes real work. harness-kit makes it portable. Package your plugins, skills, MCP servers, hooks, and conventions into a config you can apply to any harness on any machine — and share with your team in one file. Works with Claude Code today. Designed to travel. +Building a good AI setup takes real work. harness-kit makes it portable. Package your plugins, skills, MCP servers, hooks, and conventions into a config you can apply to any harness on any machine — and share with your team in one file. Works with Claude Code, Cursor, and GitHub Copilot. Designed to travel. ## Install @@ -63,7 +63,7 @@ Produces a structured explanation: summary, key components, how it connects, pat | [`data-lineage`](plugins/data-lineage/skills/data-lineage/README.md) | Trace column-level data lineage through SQL, Kafka, Spark, and JDBC codebases | `/data-lineage orders.amount` | | [`orient`](plugins/orient/skills/orient/README.md) ¹ | Topic-focused session orientation across graph, knowledge, journal, and research | `/orient auth` | | [`capture-session`](plugins/capture-session/skills/capture-session/README.md) ¹ | Capture session information into a staging file for later reflection | `/capture-session` | -| [`harness-share`](plugins/harness-share/skills/harness-export/README.md) | Export your plugin setup to `harness.yaml`, import it anywhere, and validate against the Harness Protocol v1 spec | `/harness-export` · `/harness-import` · `/harness-validate` | +| [`harness-share`](plugins/harness-share/skills/harness-export/README.md) | Export your plugin setup to `harness.yaml`, compile to native configs for Claude Code, Cursor, and Copilot, and keep them in sync | `/harness-export` · `/harness-import` · `/harness-validate` · `/harness-compile` · `/harness-sync` | ¹ Personal-workflow plugins designed for projects using the [knowledge graph + journal pattern](docs/claude-md-conventions.md). @@ -88,6 +88,8 @@ Capture your installed plugins into a `harness.yaml` file, commit it to your dot /harness-export # write harness.yaml from your current setup /harness-import harness.yaml # interactive wizard to pick what to install /harness-validate # validate harness.yaml against the Harness Protocol v1 schema +/harness-compile # compile harness.yaml to native config files for Claude Code, Cursor, and Copilot +/harness-sync # keep Claude Code, Cursor, and Copilot configuration in sync ``` The import wizard shows each plugin with its description and lets you pick a subset — your config is a starting point, not a mandate. @@ -104,7 +106,9 @@ See [`harness.yaml.example`](harness.yaml.example) for the config format. ## Using with Other Tools -harness-kit targets Claude Code, but SKILL.md files are plain markdown — copy them into any tool's instruction system. VS Code Copilot reads `CLAUDE.md` natively via the `chat.useClaudeMdFile` setting, so the conventions guide works without modification. For per-tool setup (Copilot, Cursor, Windsurf, MCP), see the [Cross-Harness setup guide](https://harnesskit.ai/docs/cross-harness/setup-guide). +harness-kit natively supports Claude Code, Cursor, and GitHub Copilot. Use `/harness-compile` to generate native config files for each tool from a single `harness.yaml`, and `/harness-sync` to keep them aligned as your setup evolves. + +SKILL.md files are plain markdown — they work in any tool's instruction system. VS Code Copilot reads `CLAUDE.md` natively via the `chat.useClaudeMdFile` setting, so the conventions guide works without modification. The [Harness Protocol spec](https://harnessprotocol.io) documents the full cross-platform target mapping. ## Conventions Guide diff --git a/marketplace/app/api/install/route.ts b/marketplace/app/api/install/route.ts new file mode 100644 index 0000000..4afb41e --- /dev/null +++ b/marketplace/app/api/install/route.ts @@ -0,0 +1,77 @@ +import { NextRequest, NextResponse } from "next/server"; +import { createClient } from "@supabase/supabase-js"; + +function getServiceSupabase() { + const url = process.env.NEXT_PUBLIC_SUPABASE_URL; + const key = process.env.SUPABASE_SERVICE_ROLE_KEY; + if (!url || !key) throw new Error("Supabase not configured"); + return createClient(url, key); +} + +interface InstallPayload { + slug: string; +} + +export async function POST(request: NextRequest) { + const supabase = getServiceSupabase(); + let payload: InstallPayload; + try { + payload = await request.json(); + } catch { + return NextResponse.json( + { error: "Invalid JSON body" }, + { status: 400 }, + ); + } + + const { slug } = payload; + + if (!slug) { + return NextResponse.json( + { error: "Missing required field: slug" }, + { status: 400 }, + ); + } + + // Atomically increment install_count to avoid race conditions + const { data: updated, error: updateError } = await supabase + .rpc("increment_install_count", { component_slug: slug }); + + if (updateError) { + // Fallback: try direct update if RPC not available + const { data: component, error: fetchError } = await supabase + .from("components") + .select("id, install_count") + .eq("slug", slug) + .single(); + + if (fetchError || !component) { + return NextResponse.json( + { error: `Plugin not found: ${slug}` }, + { status: 404 }, + ); + } + + const { error: fallbackError } = await supabase + .from("components") + .update({ install_count: (component.install_count ?? 0) + 1 }) + .eq("id", component.id); + + if (fallbackError) { + return NextResponse.json( + { error: `Failed to update install count: ${fallbackError.message}` }, + { status: 500 }, + ); + } + + return NextResponse.json({ + slug, + install_count: (component.install_count ?? 0) + 1, + }); + } + + return NextResponse.json({ + slug, + install_count: updated, + }); +} diff --git a/marketplace/app/api/register/route.ts b/marketplace/app/api/register/route.ts new file mode 100644 index 0000000..1e9e5b6 --- /dev/null +++ b/marketplace/app/api/register/route.ts @@ -0,0 +1,184 @@ +import { NextRequest, NextResponse } from "next/server"; +import { createClient } from "@supabase/supabase-js"; + +function getServiceSupabase() { + const url = process.env.NEXT_PUBLIC_SUPABASE_URL; + const key = process.env.SUPABASE_SERVICE_ROLE_KEY; + if (!url || !key) throw new Error("Supabase not configured"); + return createClient(url, key); +} + +interface RegisterPayload { + repo_url: string; + plugin_path: string; +} + +interface PluginManifest { + name: string; + description: string; + version: string; +} + +/** Fetch raw file from a GitHub repo URL + path. */ +async function fetchGitHubFile( + repoUrl: string, + filePath: string, +): Promise { + // Parse "https://github.com/owner/repo" into API URL + const match = repoUrl.match( + /github\.com\/([^/]+)\/([^/]+)/, + ); + if (!match) return null; + + const [, owner, repo] = match; + const cleanRepo = repo.replace(/\.git$/, ""); + const url = `https://api.github.com/repos/${owner}/${cleanRepo}/contents/${filePath}`; + + const headers: Record = { + Accept: "application/vnd.github.v3.raw", + "User-Agent": "harness-kit-marketplace", + }; + const token = process.env.GITHUB_TOKEN; + if (token) { + headers.Authorization = `Bearer ${token}`; + } + + const res = await fetch(url, { headers }); + if (!res.ok) return null; + return res.text(); +} + +export async function POST(request: NextRequest) { + // Require API key for registration to prevent spam + const apiKey = process.env.REGISTER_API_KEY; + if (!apiKey) { + return NextResponse.json( + { error: "Registration not configured" }, + { status: 503 }, + ); + } + + const authHeader = request.headers.get("authorization") ?? ""; + if (authHeader !== `Bearer ${apiKey}`) { + return NextResponse.json( + { error: "Invalid or missing API key" }, + { status: 401 }, + ); + } + + const supabase = getServiceSupabase(); + let payload: RegisterPayload; + try { + payload = await request.json(); + } catch { + return NextResponse.json( + { error: "Invalid JSON body" }, + { status: 400 }, + ); + } + + const { repo_url, plugin_path } = payload; + + if (!repo_url || !plugin_path) { + return NextResponse.json( + { error: "Missing required fields: repo_url, plugin_path" }, + { status: 400 }, + ); + } + + // Validate repo structure: must have plugin.json + const manifestPath = `${plugin_path}/.claude-plugin/plugin.json`; + const manifestRaw = await fetchGitHubFile(repo_url, manifestPath); + + if (!manifestRaw) { + return NextResponse.json( + { + error: `Could not find plugin manifest at ${manifestPath}. Ensure the repo is public and the path is correct.`, + }, + { status: 422 }, + ); + } + + let manifest: PluginManifest; + try { + manifest = JSON.parse(manifestRaw); + } catch { + return NextResponse.json( + { error: "Could not parse plugin.json" }, + { status: 422 }, + ); + } + + if (!manifest.name || !manifest.description || !manifest.version) { + return NextResponse.json( + { + error: + "plugin.json must include name, description, and version fields", + }, + { status: 422 }, + ); + } + + // Check for duplicate slug + const { data: existing } = await supabase + .from("components") + .select("id") + .eq("slug", manifest.name) + .single(); + + if (existing) { + return NextResponse.json( + { error: `Component with slug "${manifest.name}" already exists` }, + { status: 409 }, + ); + } + + // Try to fetch SKILL.md and README.md + const skillMd = await fetchGitHubFile( + repo_url, + `${plugin_path}/skills/${manifest.name}/SKILL.md`, + ); + const readmeMd = await fetchGitHubFile( + repo_url, + `${plugin_path}/skills/${manifest.name}/README.md`, + ); + + // Parse author from repo URL + const ownerMatch = repo_url.match(/github\.com\/([^/]+)/); + const authorName = ownerMatch ? ownerMatch[1] : "unknown"; + + // Create component with community trust tier + const { data: component, error } = await supabase + .from("components") + .insert({ + slug: manifest.name, + name: manifest.name, + type: "skill", + description: manifest.description, + trust_tier: "community", + version: manifest.version, + author: { name: authorName }, + license: "unknown", + skill_md: skillMd, + readme_md: readmeMd, + repo_url: repo_url, + install_count: 0, + }) + .select() + .single(); + + if (error) { + return NextResponse.json( + { error: `Failed to create component: ${error.message}` }, + { status: 500 }, + ); + } + + return NextResponse.json( + { + message: "Component registered successfully", + component, + }, + { status: 201 }, + ); +} diff --git a/marketplace/app/api/search/route.ts b/marketplace/app/api/search/route.ts new file mode 100644 index 0000000..378efc9 --- /dev/null +++ b/marketplace/app/api/search/route.ts @@ -0,0 +1,90 @@ +import { NextRequest, NextResponse } from "next/server"; +import { supabase } from "@/lib/supabase"; + +type SearchType = "component" | "profile"; + +export async function GET(request: NextRequest) { + const { searchParams } = request.nextUrl; + const query = searchParams.get("q") ?? ""; + const type = (searchParams.get("type") as SearchType) ?? "component"; + + if (!query) { + return NextResponse.json( + { error: "Missing required query parameter: q" }, + { status: 400 }, + ); + } + + // Sanitize and convert query to tsquery format for full-text search + const sanitizedTokens = query + .trim() + .split(/\s+/) + .map((token) => token.replace(/[!&|():*\\]/g, "")) + .filter(Boolean); + + if (sanitizedTokens.length === 0) { + return NextResponse.json( + { error: "Query contains no searchable terms" }, + { status: 400 }, + ); + } + + const tsquery = sanitizedTokens.join(" & "); + + if (type === "profile") { + const { data, error } = await supabase + .from("profiles") + .select("*") + .textSearch("fts", tsquery) + .order("name", { ascending: true }) + .limit(20); + + if (error) { + // Fallback to ilike if FTS fails — use parameterized filters + const { data: fallback, error: fallbackError } = await supabase + .from("profiles") + .select("*") + .or(`name.ilike.%${query.replace(/[,().%_\\]/g, "")}%,description.ilike.%${query.replace(/[,().%_\\]/g, "")}%`) + .order("name", { ascending: true }) + .limit(20); + + if (fallbackError) { + return NextResponse.json( + { error: `Search failed: ${fallbackError.message}` }, + { status: 500 }, + ); + } + return NextResponse.json({ type: "profile", results: fallback ?? [] }); + } + + return NextResponse.json({ type: "profile", results: data ?? [] }); + } + + // Default: search components using full-text search + const { data, error } = await supabase + .from("components") + .select("*") + .textSearch("fts", tsquery) + .order("install_count", { ascending: false }) + .limit(20); + + if (error) { + // Fallback to ilike if FTS fails — sanitize PostgREST filter metacharacters + const { data: fallback, error: fallbackError } = await supabase + .from("components") + .select("*") + .or(`name.ilike.%${query.replace(/[,().%_\\]/g, "")}%,description.ilike.%${query.replace(/[,().%_\\]/g, "")}%`) + .order("install_count", { ascending: false }) + .limit(20); + + if (fallbackError) { + return NextResponse.json( + { error: `Search failed: ${fallbackError.message}` }, + { status: 500 }, + ); + } + return NextResponse.json({ type: "component", results: fallback ?? [] }); + } + + return NextResponse.json({ type: "component", results: data ?? [] }); +} diff --git a/marketplace/app/api/sync/route.ts b/marketplace/app/api/sync/route.ts new file mode 100644 index 0000000..3c9e0a6 --- /dev/null +++ b/marketplace/app/api/sync/route.ts @@ -0,0 +1,215 @@ +import { NextRequest, NextResponse } from "next/server"; +import { createClient } from "@supabase/supabase-js"; + +function getServiceSupabase() { + const url = process.env.NEXT_PUBLIC_SUPABASE_URL; + const key = process.env.SUPABASE_SERVICE_ROLE_KEY; + if (!url || !key) throw new Error("Supabase not configured"); + return createClient(url, key); +} + +const GITHUB_WEBHOOK_SECRET = process.env.GITHUB_WEBHOOK_SECRET ?? ""; +const REPO_OWNER = "harnessprotocol"; +const REPO_NAME = "harness-kit"; + +/** Verify GitHub webhook signature using Web Crypto API. */ +async function verifySignature( + payload: string, + signature: string, +): Promise { + const encoder = new TextEncoder(); + const key = await crypto.subtle.importKey( + "raw", + encoder.encode(GITHUB_WEBHOOK_SECRET), + { name: "HMAC", hash: "SHA-256" }, + false, + ["sign"], + ); + const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(payload)); + const digest = Array.from(new Uint8Array(sig)) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); + const expected = `sha256=${digest}`; + // Constant-time comparison to prevent timing attacks + if (signature.length !== expected.length) return false; + let result = 0; + for (let i = 0; i < signature.length; i++) { + result |= signature.charCodeAt(i) ^ expected.charCodeAt(i); + } + return result === 0; +} + +/** Fetch a file from the GitHub repo. */ +async function fetchRepoFile(path: string): Promise { + const url = `https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/contents/${path}`; + const headers: Record = { + Accept: "application/vnd.github.v3.raw", + "User-Agent": "harness-kit-marketplace", + }; + const token = process.env.GITHUB_TOKEN; + if (token) { + headers.Authorization = `Bearer ${token}`; + } + + const res = await fetch(url, { headers }); + if (!res.ok) return null; + return res.text(); +} + +interface MarketplacePlugin { + name: string; + source: string; + description: string; + version: string; + author: { name: string; url?: string }; + license: string; + category?: string; + tags?: string[]; +} + +interface MarketplaceManifest { + plugins: MarketplacePlugin[]; +} + +export async function POST(request: NextRequest) { + const supabase = getServiceSupabase(); + // Verify webhook signature + const body = await request.text(); + const signature = request.headers.get("x-hub-signature-256") ?? ""; + + if (!GITHUB_WEBHOOK_SECRET) { + console.warn("GITHUB_WEBHOOK_SECRET not set — rejecting webhook request"); + return NextResponse.json( + { error: "Webhook secret not configured" }, + { status: 500 }, + ); + } + + const valid = await verifySignature(body, signature); + if (!valid) { + return NextResponse.json({ error: "Invalid signature" }, { status: 401 }); + } + + const event = JSON.parse(body); + + // Only process pushes to main branch + if (event.ref !== "refs/heads/main") { + return NextResponse.json({ message: "Ignored: not main branch" }); + } + + // Read marketplace.json from the repo + const manifestRaw = await fetchRepoFile(".claude-plugin/marketplace.json"); + if (!manifestRaw) { + return NextResponse.json( + { error: "Could not read marketplace.json" }, + { status: 500 }, + ); + } + + const manifest: MarketplaceManifest = JSON.parse(manifestRaw); + + // Process each plugin + const results: Array<{ name: string; status: string }> = []; + + for (const plugin of manifest.plugins) { + // Resolve source path: "./research" -> "plugins/research" + const pluginDir = `plugins/${plugin.source.replace("./", "")}`; + + // Try to fetch SKILL.md and README.md + const skillMd = await fetchRepoFile( + `${pluginDir}/skills/${plugin.name}/SKILL.md`, + ); + const readmeMd = await fetchRepoFile( + `${pluginDir}/skills/${plugin.name}/README.md`, + ); + + // Auto-extract tags from plugin manifest + description + const autoTags = new Set(plugin.tags ?? []); + // Add type-based tag + autoTags.add("skill"); + + // Upsert component + const { error } = await supabase + .from("components") + .upsert( + { + slug: plugin.name, + name: plugin.name, + type: "skill", + description: plugin.description, + trust_tier: "official", + version: plugin.version, + author: plugin.author, + license: plugin.license, + skill_md: skillMd, + readme_md: readmeMd, + repo_url: `https://github.com/${REPO_OWNER}/${REPO_NAME}/tree/main/${pluginDir}`, + updated_at: new Date().toISOString(), + }, + { onConflict: "slug" }, + ); + + if (error) { + results.push({ name: plugin.name, status: `error: ${error.message}` }); + } else { + // Upsert tags + for (const tagSlug of autoTags) { + await supabase + .from("tags") + .upsert({ slug: tagSlug }, { onConflict: "slug" }); + + const { data: tagRow } = await supabase + .from("tags") + .select("id") + .eq("slug", tagSlug) + .single(); + + const { data: compRow } = await supabase + .from("components") + .select("id") + .eq("slug", plugin.name) + .single(); + + if (tagRow && compRow) { + await supabase + .from("component_tags") + .upsert( + { component_id: compRow.id, tag_id: tagRow.id }, + { onConflict: "component_id,tag_id" }, + ); + } + } + + // Link component to category + if (plugin.category) { + const { data: catRow } = await supabase + .from("categories") + .select("id") + .eq("slug", plugin.category) + .single(); + + const { data: compRow2 } = await supabase + .from("components") + .select("id") + .eq("slug", plugin.name) + .single(); + + if (catRow && compRow2) { + await supabase + .from("component_categories") + .upsert( + { component_id: compRow2.id, category_id: catRow.id }, + { onConflict: "component_id,category_id" }, + ); + } + } + + results.push({ name: plugin.name, status: "synced" }); + } + } + + return NextResponse.json({ + message: "Sync complete", + results, + }); +} diff --git a/marketplace/app/globals.css b/marketplace/app/globals.css new file mode 100644 index 0000000..f1d8c73 --- /dev/null +++ b/marketplace/app/globals.css @@ -0,0 +1 @@ +@import "tailwindcss"; diff --git a/marketplace/app/layout.tsx b/marketplace/app/layout.tsx new file mode 100644 index 0000000..03b7f23 --- /dev/null +++ b/marketplace/app/layout.tsx @@ -0,0 +1,56 @@ +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import "./globals.css"; + +const inter = Inter({ subsets: ["latin"] }); + +export const metadata: Metadata = { + title: "Harness Kit Marketplace", + description: + "Skills, agents, and configuration for Claude Code — browse, search, and install plugins.", +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + +
{children}
+ + + ); +} diff --git a/marketplace/app/page.tsx b/marketplace/app/page.tsx new file mode 100644 index 0000000..910a70f --- /dev/null +++ b/marketplace/app/page.tsx @@ -0,0 +1,179 @@ +import Link from "next/link"; +import { supabase } from "@/lib/supabase"; +import type { Component } from "@/lib/types"; + +const CATEGORIES = [ + { + slug: "research-knowledge", + name: "Research & Knowledge", + description: "Build compounding knowledge bases from any source", + }, + { + slug: "code-quality", + name: "Code Quality", + description: "Reviews, explanations, and static analysis", + }, + { + slug: "data-engineering", + name: "Data Engineering", + description: "Lineage tracing, SQL analysis, and pipeline tools", + }, + { + slug: "documentation", + name: "Documentation", + description: "Generate READMEs, API docs, and changelogs", + }, + { + slug: "devops", + name: "DevOps & Shipping", + description: "CI/CD, PR workflows, and deployment automation", + }, + { + slug: "productivity", + name: "Productivity", + description: "Configuration sharing, session capture, and workflow tools", + }, +]; + +function TrustBadge({ tier }: { tier: string }) { + const colors: Record = { + official: "bg-blue-500/20 text-blue-400 border-blue-500/30", + verified: "bg-green-500/20 text-green-400 border-green-500/30", + community: "bg-gray-500/20 text-gray-400 border-gray-500/30", + }; + return ( + + {tier} + + ); +} + +function PluginRow({ component }: { component: Component }) { + const updatedDate = component.updated_at + ? new Date(component.updated_at).toLocaleDateString("en-US", { + month: "short", + day: "numeric", + year: "numeric", + }) + : null; + + return ( + +
+
+ + {component.name} + + + + {component.install_count.toLocaleString()} installs + +
+

+ {component.description} +

+
+
+
v{component.version}
+ {updatedDate &&
{updatedDate}
} +
+ + ); +} + +export default async function HomePage() { + let trending: Component[] = []; + try { + const { data } = await supabase + .from("components") + .select("*") + .order("install_count", { ascending: false }) + .limit(6); + trending = (data as Component[]) ?? []; + } catch { + // Supabase not configured yet — render with empty data + } + + return ( +
+ {/* Hero */} +
+

+ Harness Kit Marketplace +

+

+ Skills, agents, and configuration for Claude Code +

+ +
+ +

+ Browse Plugins +

+

+ Discover skills, agents, hooks, and more +

+ + +

+ Explore Profiles +

+

+ Pre-configured setups for your role +

+ +
+
+ + {/* Trending Plugins */} + {trending.length > 0 && ( +
+
+

Trending Plugins

+ + View all + +
+
+ {trending.map((c) => ( + + ))} +
+
+ )} + + {/* Featured Categories */} +
+

Featured Categories

+
+ {CATEGORIES.map((cat) => ( + +

+ {cat.name} +

+

{cat.description}

+ + ))} +
+
+
+ ); +} diff --git a/marketplace/app/plugins/[slug]/page.tsx b/marketplace/app/plugins/[slug]/page.tsx new file mode 100644 index 0000000..72e750c --- /dev/null +++ b/marketplace/app/plugins/[slug]/page.tsx @@ -0,0 +1,355 @@ +import Link from "next/link"; +import { notFound } from "next/navigation"; +import { supabase } from "@/lib/supabase"; +import type { Component, Profile, TrustTier } from "@/lib/types"; + +function TrustBadge({ tier }: { tier: TrustTier }) { + const colors: Record = { + official: "bg-blue-500/20 text-blue-400 border-blue-500/30", + verified: "bg-green-500/20 text-green-400 border-green-500/30", + community: "bg-gray-500/20 text-gray-400 border-gray-500/30", + }; + return ( + + {tier} + + ); +} + +/** + * Minimal server-side markdown-to-HTML renderer. + * Content is trusted (from our own Supabase database, not user input). + */ +function renderMarkdown(md: string): string { + const html = md + // Fenced code blocks + .replace( + /```(\w*)\n([\s\S]*?)```/g, + '
$2
', + ) + // Headings + .replace( + /^#### (.+)$/gm, + '

$1

', + ) + .replace( + /^### (.+)$/gm, + '

$1

', + ) + .replace( + /^## (.+)$/gm, + '

$1

', + ) + .replace( + /^# (.+)$/gm, + '

$1

', + ) + // Bold + .replace(/\*\*(.+?)\*\*/g, "$1") + // Inline code + .replace( + /`([^`]+)`/g, + '$1', + ) + // Links + .replace( + /\[([^\]]+)\]\(([^)]+)\)/g, + '$1', + ) + // Unordered lists + .replace(/^- (.+)$/gm, '
  • $1
  • ') + // Wrap loose lines in paragraphs (skip tags) + .replace( + /^(?!<[hluop]|$1

    ', + ); + + return html; +} + +export default async function PluginDetailPage({ + params, +}: { + params: Promise<{ slug: string }>; +}) { + const { slug } = await params; + + let component: Component | null = null; + let tags: string[] = []; + let relatedComponents: Component[] = []; + let includedInProfiles: Profile[] = []; + + try { + // Fetch the component + const { data } = await supabase + .from("components") + .select("*") + .eq("slug", slug) + .single(); + component = data as Component | null; + + if (component) { + // Fetch tags for this component + const { data: tagRows } = await supabase + .from("component_tags") + .select("tag_id, tags(slug)") + .eq("component_id", component.id); + + if (tagRows) { + tags = tagRows + .map( + (row: Record) => + (row.tags as { slug: string })?.slug ?? "", + ) + .filter(Boolean); + } + + // Fetch related components (same type, excluding self) + const { data: related } = await supabase + .from("components") + .select("*") + .eq("type", component.type) + .neq("id", component.id) + .order("install_count", { ascending: false }) + .limit(5); + relatedComponents = (related as Component[]) ?? []; + + // Fetch profiles that include this component + const { data: profileRows } = await supabase + .from("profile_components") + .select("profile_id, profiles(id, slug, name, description)") + .eq("component_id", component.id); + + if (profileRows) { + includedInProfiles = profileRows + .map( + (row: Record) => row.profiles as Profile, + ) + .filter(Boolean); + } + } + } catch { + // Supabase not configured yet + } + + if (!component) { + notFound(); + } + + // Trusted content from our own database -- see renderMarkdown comment above + const skillHtml = component.skill_md + ? renderMarkdown(component.skill_md) + : null; + const readmeHtml = component.readme_md + ? renderMarkdown(component.readme_md) + : null; + + const updatedDate = component.updated_at + ? new Date(component.updated_at).toLocaleDateString("en-US", { + month: "short", + day: "numeric", + year: "numeric", + }) + : null; + + return ( +
    + {/* Breadcrumb */} + + +
    + {/* Main column */} +
    + {/* Header */} +
    +
    +

    {component.name}

    + + + {component.type} + +
    +

    + {component.description} +

    +
    + + {/* Stats bar */} +
    + + + + + {component.install_count.toLocaleString()} installs + + v{component.version} + {component.license && {component.license}} + {updatedDate && Updated {updatedDate}} +
    + + {/* Tags */} + {tags.length > 0 && ( +
    + {tags.map((tag) => ( + + {tag} + + ))} +
    + )} + + {/* SKILL.md content -- trusted content from our own Supabase database */} + {skillHtml && ( +
    +

    Skill Definition

    +
    +
    + )} + + {/* README.md content -- trusted content from our own Supabase database */} + {readmeHtml && ( +
    +

    Documentation

    +
    +
    + )} +
    + + {/* Sidebar */} + +
    +
    + ); +} diff --git a/marketplace/app/plugins/page.tsx b/marketplace/app/plugins/page.tsx new file mode 100644 index 0000000..1681850 --- /dev/null +++ b/marketplace/app/plugins/page.tsx @@ -0,0 +1,351 @@ +import Link from "next/link"; +import { supabase } from "@/lib/supabase"; +import type { Component, ComponentType, TrustTier } from "@/lib/types"; + +const CATEGORIES = [ + { slug: "research-knowledge", name: "Research & Knowledge" }, + { slug: "code-quality", name: "Code Quality" }, + { slug: "data-engineering", name: "Data Engineering" }, + { slug: "documentation", name: "Documentation" }, + { slug: "devops", name: "DevOps & Shipping" }, + { slug: "productivity", name: "Productivity" }, +]; + +const COMPONENT_TYPES: ComponentType[] = [ + "skill", + "agent", + "hook", + "script", + "knowledge", + "rules", +]; + +function TrustBadge({ tier }: { tier: TrustTier }) { + const colors: Record = { + official: "bg-blue-500/20 text-blue-400 border-blue-500/30", + verified: "bg-green-500/20 text-green-400 border-green-500/30", + community: "bg-gray-500/20 text-gray-400 border-gray-500/30", + }; + return ( + + {tier} + + ); +} + +interface SearchParams { + q?: string; + category?: string; + type?: string; + trust?: string; + tag?: string; + sort?: string; +} + +export default async function PluginsPage({ + searchParams, +}: { + searchParams: Promise; +}) { + const params = await searchParams; + const query = params.q ?? ""; + const selectedCategory = params.category ?? ""; + const selectedType = params.type ?? ""; + const selectedTrust = params.trust ?? ""; + const selectedTag = params.tag ?? ""; + const sortBy = params.sort ?? "installs"; + + let components: Component[] = []; + try { + let q = supabase.from("components").select("*"); + + if (query) { + q = q.ilike("name", `%${query}%`); + } + if (selectedType) { + q = q.eq("type", selectedType); + } + if (selectedTrust) { + q = q.eq("trust_tier", selectedTrust); + } + + if (sortBy === "recent") { + q = q.order("updated_at", { ascending: false }); + } else { + q = q.order("install_count", { ascending: false }); + } + + const { data } = await q; + let results = (data as Component[]) ?? []; + + // Apply category filter via join table + if (selectedCategory && results.length > 0) { + const { data: catRow } = await supabase + .from("categories") + .select("id") + .eq("slug", selectedCategory) + .single(); + + if (catRow) { + const { data: linked } = await supabase + .from("component_categories") + .select("component_id") + .eq("category_id", catRow.id); + + const ids = new Set((linked ?? []).map((r: { component_id: string }) => r.component_id)); + results = results.filter((c) => ids.has(c.id)); + } + } + + // Apply tag filter via join table + if (selectedTag && results.length > 0) { + const { data: tagRow } = await supabase + .from("tags") + .select("id") + .eq("slug", selectedTag) + .single(); + + if (tagRow) { + const { data: linked } = await supabase + .from("component_tags") + .select("component_id") + .eq("tag_id", tagRow.id); + + const ids = new Set((linked ?? []).map((r: { component_id: string }) => r.component_id)); + results = results.filter((c) => ids.has(c.id)); + } + } + + components = results; + } catch { + // Supabase not configured yet + } + + function buildUrl(overrides: Record) { + const merged = { ...params, ...overrides }; + const qs = Object.entries(merged) + .filter(([, v]) => v) + .map(([k, v]) => `${k}=${encodeURIComponent(v)}`) + .join("&"); + return `/plugins${qs ? `?${qs}` : ""}`; + } + + return ( +
    +

    Plugins

    + + {/* Search bar */} +
    +
    + + {selectedCategory && ( + + )} + {selectedType && ( + + )} + {selectedTrust && ( + + )} + {selectedTag && ( + + )} + +
    +
    + +
    + {/* Sidebar filters */} + + + {/* Plugin list */} +
    + {/* Sort controls */} +
    + Sort: + + Installs + + + Recent + +
    + + {components.length === 0 ? ( +
    +

    + No plugins found. Connect Supabase to load data. +

    +
    + ) : ( +
    + {components.map((component) => { + const updatedDate = component.updated_at + ? new Date(component.updated_at).toLocaleDateString( + "en-US", + { month: "short", day: "numeric" }, + ) + : null; + + return ( + +
    +
    + + {component.name} + + + + + + {component.install_count.toLocaleString()} + + {component.author && ( + + {component.author.name} + + )} + + + {component.type} + +
    +

    + {component.description} +

    +
    +
    +
    v{component.version}
    + {updatedDate && ( +
    {updatedDate}
    + )} +
    + + ); + })} +
    + )} +
    +
    +
    + ); +} diff --git a/marketplace/app/profiles/[slug]/page.tsx b/marketplace/app/profiles/[slug]/page.tsx new file mode 100644 index 0000000..de9c070 --- /dev/null +++ b/marketplace/app/profiles/[slug]/page.tsx @@ -0,0 +1,237 @@ +import Link from "next/link"; +import { notFound } from "next/navigation"; +import { supabase } from "@/lib/supabase"; +import type { Component, Profile, TrustTier } from "@/lib/types"; + +function TrustBadge({ tier }: { tier: TrustTier }) { + const colors: Record = { + official: "bg-blue-500/20 text-blue-400 border-blue-500/30", + verified: "bg-green-500/20 text-green-400 border-green-500/30", + community: "bg-gray-500/20 text-gray-400 border-gray-500/30", + }; + return ( + + {tier} + + ); +} + +export default async function ProfileDetailPage({ + params, +}: { + params: Promise<{ slug: string }>; +}) { + const { slug } = await params; + + let profile: Profile | null = null; + let components: Component[] = []; + let tags: string[] = []; + let relatedProfiles: Profile[] = []; + + try { + // Fetch the profile + const { data } = await supabase + .from("profiles") + .select("*") + .eq("slug", slug) + .single(); + profile = data as Profile | null; + + if (profile) { + // Fetch components in this profile + const { data: componentRows } = await supabase + .from("profile_components") + .select( + "pinned_version, components(id, slug, name, description, type, trust_tier, version, install_count)", + ) + .eq("profile_id", profile.id); + + if (componentRows) { + components = componentRows + .map( + (row: Record) => row.components as Component, + ) + .filter(Boolean); + } + + // Fetch tags for this profile + const { data: tagRows } = await supabase + .from("profile_tags") + .select("tag_id, tags(slug)") + .eq("profile_id", profile.id); + + if (tagRows) { + tags = tagRows + .map( + (row: Record) => + (row.tags as { slug: string })?.slug ?? "", + ) + .filter(Boolean); + } + + // Fetch related profiles (excluding self) + const { data: related } = await supabase + .from("profiles") + .select("*") + .neq("id", profile.id) + .order("name", { ascending: true }) + .limit(3); + relatedProfiles = (related as Profile[]) ?? []; + } + } catch { + // Supabase not configured yet + } + + if (!profile) { + notFound(); + } + + return ( +
    + {/* Breadcrumb */} + + + {/* Header */} +
    +
    +

    {profile.name}

    + +
    +

    {profile.description}

    + {profile.author && ( +

    + by{" "} + {profile.author.url ? ( + + {profile.author.name} + + ) : ( + profile.author.name + )} +

    + )} +
    + + {/* Tags */} + {tags.length > 0 && ( +
    + {tags.map((tag) => ( + + {tag} + + ))} +
    + )} + + {/* Components in this profile */} +
    +

    + Plugins ({components.length}) +

    + {components.length === 0 ? ( +

    + No plugins listed for this profile yet. +

    + ) : ( +
    + {components.map((component) => ( + +
    +
    +

    + {component.name} +

    + + + {component.type} + +
    +

    + {component.description} +

    +
    + + v{component.version} + + + ))} +
    + )} +
    + + {/* harness.yaml preview */} + {profile.harness_yaml_template && ( +
    +

    harness.yaml

    +
    +            {profile.harness_yaml_template}
    +          
    +
    + )} + + {/* Install Profile */} +
    +

    + Install Profile +

    +

    + Copy the harness.yaml above into your project, or install components + individually: +

    +
    + {components.map((component) => ( + + /plugin install {component.slug}@harness-kit + + ))} +
    +
    + + {/* Related Profiles */} + {relatedProfiles.length > 0 && ( +
    +

    Other Profiles

    +
    + {relatedProfiles.map((related) => ( + +

    + {related.name} +

    +

    + {related.description} +

    + + ))} +
    +
    + )} +
    + ); +} diff --git a/marketplace/app/profiles/page.tsx b/marketplace/app/profiles/page.tsx new file mode 100644 index 0000000..7be0be1 --- /dev/null +++ b/marketplace/app/profiles/page.tsx @@ -0,0 +1,118 @@ +import Link from "next/link"; +import { supabase } from "@/lib/supabase"; +import type { Profile, TrustTier } from "@/lib/types"; + +function TrustBadge({ tier }: { tier: TrustTier }) { + const colors: Record = { + official: "bg-blue-500/20 text-blue-400 border-blue-500/30", + verified: "bg-green-500/20 text-green-400 border-green-500/30", + community: "bg-gray-500/20 text-gray-400 border-gray-500/30", + }; + return ( + + {tier} + + ); +} + +interface ProfileWithCounts extends Profile { + component_count?: number; +} + +interface SearchParams { + q?: string; +} + +export default async function ProfilesPage({ + searchParams, +}: { + searchParams: Promise; +}) { + const params = await searchParams; + const query = params.q ?? ""; + + let profiles: ProfileWithCounts[] = []; + try { + let q = supabase.from("profiles").select("*"); + + if (query) { + q = q.ilike("name", `%${query}%`); + } + + q = q.order("name", { ascending: true }); + + const { data } = await q; + profiles = (data as ProfileWithCounts[]) ?? []; + } catch { + // Supabase not configured yet + } + + return ( +
    +

    Profiles

    + + {/* Search bar */} +
    +
    + + +
    +
    + + {/* Profile list */} + {profiles.length === 0 ? ( +
    +

    + No profiles found. Connect Supabase to load data. +

    +
    + ) : ( +
    + {profiles.map((profile) => ( + +
    +
    + + {profile.name} + + + {profile.author && ( + + {profile.author.name} + + )} + {profile.component_count !== undefined && ( + + {profile.component_count} plugin + {profile.component_count !== 1 ? "s" : ""} included + + )} +
    +

    + {profile.description} +

    +
    + + ))} +
    + )} +
    + ); +} diff --git a/marketplace/lib/supabase.ts b/marketplace/lib/supabase.ts new file mode 100644 index 0000000..b5327f2 --- /dev/null +++ b/marketplace/lib/supabase.ts @@ -0,0 +1,25 @@ +import { createClient, type SupabaseClient } from "@supabase/supabase-js"; + +let _supabase: SupabaseClient | null = null; + +export function getSupabase(): SupabaseClient { + if (!_supabase) { + const url = process.env.NEXT_PUBLIC_SUPABASE_URL; + const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; + if (!url || !key) { + throw new Error("Supabase not configured"); + } + _supabase = createClient(url, key); + } + return _supabase; +} + +/** + * Convenience alias — pages should wrap calls in try/catch + * since Supabase may not be configured during build or development. + */ +export const supabase = new Proxy({} as SupabaseClient, { + get(_target, prop) { + return getSupabase()[prop as keyof SupabaseClient]; + }, +}); diff --git a/marketplace/lib/types.ts b/marketplace/lib/types.ts new file mode 100644 index 0000000..89cc654 --- /dev/null +++ b/marketplace/lib/types.ts @@ -0,0 +1,8 @@ +export type { + Component, + Profile, + Category, + Tag, + TrustTier, + ComponentType, +} from "@harness-kit/shared"; diff --git a/marketplace/next-env.d.ts b/marketplace/next-env.d.ts new file mode 100644 index 0000000..830fb59 --- /dev/null +++ b/marketplace/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/marketplace/next.config.ts b/marketplace/next.config.ts new file mode 100644 index 0000000..dbedcdb --- /dev/null +++ b/marketplace/next.config.ts @@ -0,0 +1,5 @@ +import type { NextConfig } from "next"; + +const config: NextConfig = {}; + +export default config; diff --git a/marketplace/package.json b/marketplace/package.json new file mode 100644 index 0000000..a1054fe --- /dev/null +++ b/marketplace/package.json @@ -0,0 +1,26 @@ +{ + "name": "harness-kit-marketplace", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev --port 3001", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "next": "^15.0.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "@supabase/supabase-js": "^2.0.0", + "@harness-kit/shared": "workspace:*" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "typescript": "^5.0.0", + "tailwindcss": "^4.0.0", + "@tailwindcss/postcss": "^4.0.0", + "postcss": "^8.0.0" + } +} diff --git a/marketplace/postcss.config.mjs b/marketplace/postcss.config.mjs new file mode 100644 index 0000000..61e3684 --- /dev/null +++ b/marketplace/postcss.config.mjs @@ -0,0 +1,7 @@ +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/marketplace/supabase/migrations/00001_initial_schema.sql b/marketplace/supabase/migrations/00001_initial_schema.sql new file mode 100644 index 0000000..27455d3 --- /dev/null +++ b/marketplace/supabase/migrations/00001_initial_schema.sql @@ -0,0 +1,137 @@ +-- Enable extensions +create extension if not exists "uuid-ossp"; + +-- Categories (curated, small list) +create table categories ( + id uuid primary key default uuid_generate_v4(), + slug text unique not null, + name text not null, + display_order int not null default 0 +); + +-- Tags (freeform, auto-populated) +create table tags ( + id uuid primary key default uuid_generate_v4(), + slug text unique not null +); + +-- Components (the atomic distributable unit) +create table components ( + id uuid primary key default uuid_generate_v4(), + slug text unique not null, + name text not null, + type text not null check (type in ('skill', 'agent', 'hook', 'script', 'knowledge', 'rules')), + description text not null, + trust_tier text not null default 'community' check (trust_tier in ('official', 'verified', 'community')), + version text not null, + author jsonb not null default '{}', + license text not null default 'Apache-2.0', + skill_md text, + readme_md text, + repo_url text, + install_count int not null default 0, + created_at timestamptz not null default now(), + updated_at timestamptz not null default now() +); + +-- Profiles (named harness.yaml templates) +create table profiles ( + id uuid primary key default uuid_generate_v4(), + slug text unique not null, + name text not null, + description text not null, + author jsonb not null default '{}', + trust_tier text not null default 'community' check (trust_tier in ('official', 'verified', 'community')), + harness_yaml_template text not null, + created_at timestamptz not null default now(), + updated_at timestamptz not null default now() +); + +-- Join tables +create table component_categories ( + component_id uuid not null references components(id) on delete cascade, + category_id uuid not null references categories(id) on delete cascade, + primary key (component_id, category_id) +); + +create table component_tags ( + component_id uuid not null references components(id) on delete cascade, + tag_id uuid not null references tags(id) on delete cascade, + primary key (component_id, tag_id) +); + +create table profile_components ( + profile_id uuid not null references profiles(id) on delete cascade, + component_id uuid not null references components(id) on delete cascade, + pinned_version text not null, + primary key (profile_id, component_id) +); + +create table profile_categories ( + profile_id uuid not null references profiles(id) on delete cascade, + category_id uuid not null references categories(id) on delete cascade, + primary key (profile_id, category_id) +); + +create table profile_tags ( + profile_id uuid not null references profiles(id) on delete cascade, + tag_id uuid not null references tags(id) on delete cascade, + primary key (profile_id, tag_id) +); + +-- Full-text search index on components +alter table components add column fts tsvector + generated always as ( + setweight(to_tsvector('english', coalesce(name, '')), 'A') || + setweight(to_tsvector('english', coalesce(description, '')), 'B') || + setweight(to_tsvector('english', coalesce(skill_md, '')), 'C') || + setweight(to_tsvector('english', coalesce(readme_md, '')), 'D') + ) stored; + +create index idx_components_fts on components using gin(fts); + +-- Full-text search index on profiles +alter table profiles add column fts tsvector + generated always as ( + setweight(to_tsvector('english', coalesce(name, '')), 'A') || + setweight(to_tsvector('english', coalesce(description, '')), 'B') + ) stored; + +create index idx_profiles_fts on profiles using gin(fts); + +-- Index for common queries +create index idx_components_type on components(type); +create index idx_components_trust_tier on components(trust_tier); +create index idx_components_slug on components(slug); +create index idx_profiles_slug on profiles(slug); + +-- Updated_at trigger +create or replace function update_updated_at() +returns trigger as $$ +begin + new.updated_at = now(); + return new; +end; +$$ language plpgsql; + +create trigger components_updated_at + before update on components + for each row execute function update_updated_at(); + +create trigger profiles_updated_at + before update on profiles + for each row execute function update_updated_at(); + +-- Atomic install count increment (avoids read-then-write race condition) +create or replace function increment_install_count(component_slug text) +returns int as $$ +declare + new_count int; +begin + update components + set install_count = install_count + 1 + where slug = component_slug + returning install_count into new_count; + return new_count; +end; +$$ language plpgsql; diff --git a/marketplace/supabase/seed.ts b/marketplace/supabase/seed.ts new file mode 100644 index 0000000..87ff4dd --- /dev/null +++ b/marketplace/supabase/seed.ts @@ -0,0 +1,289 @@ +/** + * Seed script for the Harness Kit marketplace Supabase database. + * + * Reads marketplace.json and plugin SKILL.md / README.md files from the repo, + * then upserts categories, tags, components, and join records. + * + * Usage: + * SUPABASE_URL=... SUPABASE_SERVICE_ROLE_KEY=... npx tsx seed.ts + */ + +import fs from "node:fs"; +import path from "node:path"; +import { createClient } from "@supabase/supabase-js"; + +// --------------------------------------------------------------------------- +// Config +// --------------------------------------------------------------------------- + +const SUPABASE_URL = process.env.SUPABASE_URL; +const SUPABASE_SERVICE_ROLE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY; + +if (!SUPABASE_URL || !SUPABASE_SERVICE_ROLE_KEY) { + console.error( + "Missing required environment variables: SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY" + ); + process.exit(1); +} + +const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY); + +// Paths are relative to this file (marketplace/supabase/seed.ts) +const REPO_ROOT = path.resolve(import.meta.dirname, "../.."); +const MARKETPLACE_JSON_PATH = path.join( + REPO_ROOT, + ".claude-plugin/marketplace.json" +); +const PLUGINS_DIR = path.join(REPO_ROOT, "plugins"); + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +interface MarketplaceJson { + categories: { slug: string; name: string; display_order: number }[]; + plugins: { + name: string; + source: string; + description: string; + version: string; + author: { name: string }; + license: string; + category: string; + tags: string[]; + }[]; +} + +function readFileOrNull(filePath: string): string | null { + try { + return fs.readFileSync(filePath, "utf-8"); + } catch { + return null; + } +} + +/** + * Reads and concatenates all SKILL.md files found under a plugin's skills/ directory. + * Most plugins have a single skill; harness-share has several. + */ +function readSkillMds(pluginName: string): string | null { + const skillsDir = path.join(PLUGINS_DIR, pluginName, "skills"); + if (!fs.existsSync(skillsDir)) return null; + + const skillDirs = fs + .readdirSync(skillsDir, { withFileTypes: true }) + .filter((d) => d.isDirectory()) + .map((d) => d.name) + .sort(); + + const parts: string[] = []; + for (const dir of skillDirs) { + const content = readFileOrNull(path.join(skillsDir, dir, "SKILL.md")); + if (content) parts.push(content); + } + return parts.length > 0 ? parts.join("\n\n---\n\n") : null; +} + +/** + * Reads and concatenates all README.md files found under a plugin's skills/ directory. + */ +function readReadmeMds(pluginName: string): string | null { + const skillsDir = path.join(PLUGINS_DIR, pluginName, "skills"); + if (!fs.existsSync(skillsDir)) return null; + + const skillDirs = fs + .readdirSync(skillsDir, { withFileTypes: true }) + .filter((d) => d.isDirectory()) + .map((d) => d.name) + .sort(); + + const parts: string[] = []; + for (const dir of skillDirs) { + const content = readFileOrNull(path.join(skillsDir, dir, "README.md")); + if (content) parts.push(content); + } + return parts.length > 0 ? parts.join("\n\n---\n\n") : null; +} + +// --------------------------------------------------------------------------- +// Seed steps +// --------------------------------------------------------------------------- + +async function seedCategories( + categories: MarketplaceJson["categories"] +): Promise> { + console.log(`Seeding ${categories.length} categories...`); + + const { data, error } = await supabase + .from("categories") + .upsert(categories, { onConflict: "slug" }) + .select("id, slug"); + + if (error) { + throw new Error(`Failed to seed categories: ${error.message}`); + } + + const slugToId = new Map(); + for (const row of data!) { + slugToId.set(row.slug, row.id); + } + + console.log(` Seeded categories: ${[...slugToId.keys()].join(", ")}`); + return slugToId; +} + +async function seedTags(tagSlugs: string[]): Promise> { + console.log(`Seeding ${tagSlugs.length} tags...`); + + const rows = tagSlugs.map((slug) => ({ slug })); + + const { data, error } = await supabase + .from("tags") + .upsert(rows, { onConflict: "slug" }) + .select("id, slug"); + + if (error) { + throw new Error(`Failed to seed tags: ${error.message}`); + } + + const slugToId = new Map(); + for (const row of data!) { + slugToId.set(row.slug, row.id); + } + + console.log(` Seeded ${slugToId.size} unique tags`); + return slugToId; +} + +async function seedComponents( + plugins: MarketplaceJson["plugins"], + categoryMap: Map, + tagMap: Map +): Promise { + console.log(`Seeding ${plugins.length} components...`); + + for (const plugin of plugins) { + const skillMd = readSkillMds(plugin.name); + const readmeMd = readReadmeMds(plugin.name); + + // Humanize the plugin name: "data-lineage" -> "Data Lineage" + const displayName = plugin.name + .split("-") + .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) + .join(" "); + + const component = { + slug: plugin.name, + name: displayName, + type: "skill" as const, + description: plugin.description, + trust_tier: "official" as const, + version: plugin.version, + author: plugin.author, + license: plugin.license, + skill_md: skillMd, + readme_md: readmeMd, + repo_url: `https://github.com/harnessprotocol/harness-kit/tree/main/plugins/${plugin.name}`, + }; + + const { data, error } = await supabase + .from("components") + .upsert(component, { onConflict: "slug" }) + .select("id") + .single(); + + if (error) { + throw new Error( + `Failed to upsert component "${plugin.name}": ${error.message}` + ); + } + + const componentId = data!.id; + console.log(` Component "${plugin.name}" -> ${componentId}`); + + // -- Category join -- + const categoryId = categoryMap.get(plugin.category); + if (categoryId) { + const { error: catErr } = await supabase + .from("component_categories") + .upsert( + { component_id: componentId, category_id: categoryId }, + { onConflict: "component_id,category_id" } + ); + + if (catErr) { + console.warn( + ` Warning: failed to link category "${plugin.category}" for "${plugin.name}": ${catErr.message}` + ); + } + } else { + console.warn( + ` Warning: category "${plugin.category}" not found for plugin "${plugin.name}"` + ); + } + + // -- Tag joins -- + for (const tagSlug of plugin.tags) { + const tagId = tagMap.get(tagSlug); + if (!tagId) { + console.warn( + ` Warning: tag "${tagSlug}" not found for plugin "${plugin.name}"` + ); + continue; + } + + const { error: tagErr } = await supabase + .from("component_tags") + .upsert( + { component_id: componentId, tag_id: tagId }, + { onConflict: "component_id,tag_id" } + ); + + if (tagErr) { + console.warn( + ` Warning: failed to link tag "${tagSlug}" for "${plugin.name}": ${tagErr.message}` + ); + } + } + } +} + +// --------------------------------------------------------------------------- +// Main +// --------------------------------------------------------------------------- + +async function main(): Promise { + console.log("=== Harness Kit Marketplace Seed ===\n"); + + // 1. Read marketplace.json + console.log(`Reading ${MARKETPLACE_JSON_PATH}`); + const raw = fs.readFileSync(MARKETPLACE_JSON_PATH, "utf-8"); + const marketplace: MarketplaceJson = JSON.parse(raw); + console.log( + ` Found ${marketplace.categories.length} categories, ${marketplace.plugins.length} plugins\n` + ); + + // 2. Seed categories + const categoryMap = await seedCategories(marketplace.categories); + console.log(); + + // 3. Collect and seed tags (deduplicated) + const allTags = new Set(); + for (const plugin of marketplace.plugins) { + for (const tag of plugin.tags) { + allTags.add(tag); + } + } + const tagMap = await seedTags([...allTags].sort()); + console.log(); + + // 4. Seed components with join records + await seedComponents(marketplace.plugins, categoryMap, tagMap); + + console.log("\n=== Seed complete ==="); +} + +main().catch((err) => { + console.error("Seed failed:", err); + process.exit(1); +}); diff --git a/marketplace/tailwind.config.ts b/marketplace/tailwind.config.ts new file mode 100644 index 0000000..b2bee9c --- /dev/null +++ b/marketplace/tailwind.config.ts @@ -0,0 +1,7 @@ +import type { Config } from "tailwindcss"; + +const config: Config = { + content: ["./app/**/*.tsx"], +}; + +export default config; diff --git a/marketplace/tsconfig.json b/marketplace/tsconfig.json new file mode 100644 index 0000000..d8b9323 --- /dev/null +++ b/marketplace/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..eaf221e --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "name": "harness-kit", + "private": true, + "packageManager": "pnpm@10.29.1", + "scripts": { + "dev:website": "pnpm --filter harness-kit-docs dev", + "dev:marketplace": "pnpm --filter harness-kit-marketplace dev", + "build:website": "pnpm --filter harness-kit-docs build", + "build:marketplace": "pnpm --filter harness-kit-marketplace build", + "build": "pnpm -r build" + } +} diff --git a/packages/shared/package.json b/packages/shared/package.json new file mode 100644 index 0000000..daf8723 --- /dev/null +++ b/packages/shared/package.json @@ -0,0 +1,21 @@ +{ + "name": "@harness-kit/shared", + "version": "0.1.0", + "private": true, + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "scripts": { + "build": "tsc", + "dev": "tsc --watch" + }, + "devDependencies": { + "typescript": "^5.0.0" + } +} diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts new file mode 100644 index 0000000..eb749b3 --- /dev/null +++ b/packages/shared/src/index.ts @@ -0,0 +1,19 @@ +export type { + Component, + ComponentType, + TrustTier, + Profile, + Category, + Tag, + Author, + ComponentCategory, + ComponentTag, + ProfileComponent, + ProfileCategory, + ProfileTag, + PluginManifest, + MarketplaceManifest, + MarketplacePlugin, + MarketplaceCategory, + ProfileYaml, +} from "./types.js"; diff --git a/packages/shared/src/types.ts b/packages/shared/src/types.ts new file mode 100644 index 0000000..c74cffc --- /dev/null +++ b/packages/shared/src/types.ts @@ -0,0 +1,158 @@ +// ── Core enums ────────────────────────────────────────────── + +export type ComponentType = + | "skill" + | "agent" + | "hook" + | "script" + | "knowledge" + | "rules"; + +export type TrustTier = "official" | "verified" | "community"; + +// ── Core entities ─────────────────────────────────────────── + +export interface Author { + name: string; + url?: string; +} + +export interface Component { + id: string; + slug: string; + name: string; + type: ComponentType; + description: string; + trust_tier: TrustTier; + version: string; + author: Author; + license: string; + skill_md: string | null; + readme_md: string | null; + repo_url: string | null; + install_count: number; + created_at: string; + updated_at: string; +} + +export interface Profile { + id: string; + slug: string; + name: string; + description: string; + author: Author; + trust_tier: TrustTier; + harness_yaml_template: string; + created_at: string; + updated_at: string; +} + +export interface Category { + id: string; + slug: string; + name: string; + display_order: number; +} + +export interface Tag { + id: string; + slug: string; +} + +// ── Join tables ───────────────────────────────────────────── + +export interface ComponentCategory { + component_id: string; + category_id: string; +} + +export interface ComponentTag { + component_id: string; + tag_id: string; +} + +export interface ProfileComponent { + profile_id: string; + component_id: string; + pinned_version: string; +} + +export interface ProfileCategory { + profile_id: string; + category_id: string; +} + +export interface ProfileTag { + profile_id: string; + tag_id: string; +} + +// ── Profile YAML (harness profile definitions) ───────────── + +export interface ProfileYaml { + name: string; + description: string; + author: Author; + components: Array<{ + name: string; + version: string; + }>; + knowledge?: { + backend: string; + seed_docs?: Array<{ + topic: string; + description: string; + }>; + }; + rules?: string[]; +} + +// ── Plugin manifest (plugin.json) ─────────────────────────── + +export interface PluginManifest { + name: string; + description: string; + version: string; + developed_with?: string; + tags?: string[]; + category?: string; + requires?: { + env?: Array<{ + name: string; + description: string; + required: boolean; + sensitive: boolean; + when: string; + }>; + }; +} + +// ── Marketplace manifest (marketplace.json) ───────────────── + +export interface MarketplaceCategory { + slug: string; + name: string; + display_order: number; +} + +export interface MarketplacePlugin { + name: string; + source: string; + description: string; + version: string; + author: Author; + license: string; + category?: string; + tags?: string[]; +} + +export interface MarketplaceManifest { + name: string; + owner: { name: string }; + metadata: { + description: string; + pluginRoot: string; + }; + categories: MarketplaceCategory[]; + plugins: MarketplacePlugin[]; +} diff --git a/packages/shared/tsconfig.json b/packages/shared/tsconfig.json new file mode 100644 index 0000000..29b150e --- /dev/null +++ b/packages/shared/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src"], + "exclude": ["dist", "node_modules"] +} diff --git a/plugins/capture-session/.claude-plugin/plugin.json b/plugins/capture-session/.claude-plugin/plugin.json index f932623..5f9168d 100644 --- a/plugins/capture-session/.claude-plugin/plugin.json +++ b/plugins/capture-session/.claude-plugin/plugin.json @@ -1,5 +1,7 @@ { "name": "capture-session", "description": "Capture session information into a staging file for later reflection and knowledge graph processing", - "version": "0.2.0" + "version": "0.2.0", + "category": "research-knowledge", + "tags": ["session-capture", "knowledge-graph", "reflection", "staging"] } diff --git a/plugins/data-lineage/.claude-plugin/plugin.json b/plugins/data-lineage/.claude-plugin/plugin.json index 406dfae..64ce20a 100644 --- a/plugins/data-lineage/.claude-plugin/plugin.json +++ b/plugins/data-lineage/.claude-plugin/plugin.json @@ -1,5 +1,7 @@ { "name": "data-lineage", "description": "Trace column-level data lineage through SQL, Kafka, Spark, and JDBC codebases", - "version": "0.2.0" + "version": "0.2.0", + "category": "data-engineering", + "tags": ["sql", "data-lineage", "kafka", "spark", "jdbc", "column-level"] } diff --git a/plugins/docgen/.claude-plugin/plugin.json b/plugins/docgen/.claude-plugin/plugin.json index 1bc4a13..3c133ce 100644 --- a/plugins/docgen/.claude-plugin/plugin.json +++ b/plugins/docgen/.claude-plugin/plugin.json @@ -2,5 +2,7 @@ "name": "docgen", "description": "Generate or update README, API docs, architecture overview, or changelog — always confirms before writing", "version": "0.2.0", + "category": "documentation", + "tags": ["documentation", "readme", "api-docs", "changelog", "architecture"], "developed_with": "claude-sonnet-4" } diff --git a/plugins/explain/.claude-plugin/plugin.json b/plugins/explain/.claude-plugin/plugin.json index 4bb0f3f..3f05c79 100644 --- a/plugins/explain/.claude-plugin/plugin.json +++ b/plugins/explain/.claude-plugin/plugin.json @@ -2,5 +2,7 @@ "name": "explain", "description": "Structured code explainer — layered explanations of files, functions, directories, or concepts", "version": "0.2.0", + "category": "code-quality", + "tags": ["code-explanation", "learning", "walkthrough", "codebase-understanding"], "developed_with": "claude-sonnet-4" } diff --git a/plugins/harness-share/.claude-plugin/plugin.json b/plugins/harness-share/.claude-plugin/plugin.json index b630b44..d70a2ae 100644 --- a/plugins/harness-share/.claude-plugin/plugin.json +++ b/plugins/harness-share/.claude-plugin/plugin.json @@ -1,5 +1,7 @@ { "name": "harness-share", - "description": "Export, share, and import your harness-kit configuration — capture installed plugins into harness.yaml and restore them on any machine", - "version": "0.2.0" + "description": "Compile, export, import, and sync harness configurations across Claude Code, Cursor, and GitHub Copilot", + "version": "0.3.0", + "category": "productivity", + "tags": ["configuration", "export", "import", "yaml", "cross-platform"] } diff --git a/plugins/harness-share/skills/harness-compile/README.md b/plugins/harness-share/skills/harness-compile/README.md new file mode 100644 index 0000000..7948501 --- /dev/null +++ b/plugins/harness-share/skills/harness-compile/README.md @@ -0,0 +1,96 @@ +# harness-compile + +Compiles a `harness.yaml` into native config files for Claude Code, Cursor, and GitHub Copilot. One source file, all your tools. + +## Usage + +``` +/harness-compile +/harness-compile path/to/harness.yaml +/harness-compile --target cursor +/harness-compile --dry-run +/harness-compile --clean +``` + +Without an argument, looks for `harness.yaml` in the current directory. + +## What It Does + +1. Finds and parses your `harness.yaml` +2. Detects which AI tools are present in the project (or accepts `--target` to skip detection) +3. Compiles instruction slots to each tool's native file format +4. Writes MCP server configs for each target +5. Copies installed skills to each target's skill directory +6. Writes permissions to `.claude/settings.json` (Claude Code) or instruction text (Cursor/Copilot) +7. Prints a compilation report + +## Flags + +| Flag | Description | +|------|-------------| +| `--target ` | Compile for a specific target: `claude-code`, `cursor`, `copilot`, or `all` | +| `--dry-run` | Preview all output without writing any files | +| `--clean` | Remove orphaned marker blocks from prior harness profiles | +| `--verbose` | Show skipped slots and extra detail in the compilation report | + +## Output Files + +| Harness slot | Claude Code | Copilot | Cursor | +|---|---|---|---| +| `operational` | `CLAUDE.md` | `.github/copilot-instructions.md` | `.cursor/rules/harness.mdc` | +| `behavioral` | `AGENT.md` | `.github/instructions/behavioral.instructions.md` | `.cursor/rules/behavioral.mdc` | +| `identity` | `SOUL.md` | (omitted) | (omitted) | +| MCP servers | `.mcp.json` | `.vscode/mcp.json` | `.cursor/mcp.json` | +| Permissions | `.claude/settings.json` | instruction text | instruction text | + +## Merge Safety + +Generated content is wrapped in section markers so re-compilation only updates the harness-managed sections — your manual customizations outside the markers are never touched: + +``` + +...generated content updated on every compile... + +``` + +The `{name}` in markers comes from `metadata.name` in your harness.yaml (defaults to `default`). + +## Import Modes + +Control how the compiler handles existing files via `instructions.import-mode` in your harness.yaml: + +| Mode | Behavior | +|------|----------| +| `merge` (default) | Append marker block at end of file; update only between markers on re-compile | +| `replace` | Overwrite entire file with generated content (requires confirmation) | +| `skip` | Do not write or modify this slot's file | + +## Example Output + +``` +Compiled harness: data-engineer (v1.2.0) +Targets: claude-code, cursor + + CLAUDE.md operational merge 48 lines added + AGENT.md behavioral merge 12 lines added + .mcp.json mcp-servers —— 2 servers + .claude/settings.json permissions —— 4 allowed, 1 denied + .cursor/rules/harness.mdc operational merge 48 lines added + .cursor/rules/behavioral.mdc behavioral merge 12 lines added + .cursor/mcp.json mcp-servers —— 2 servers + + Warnings: + permissions.tools.deny is not machine-enforceable for target 'cursor'. +``` + +## Permissions + +Claude Code permissions compile to `.claude/settings.json` with `allow`, `deny`, and `additionalDirectories` keys — fully machine-enforced. + +Cursor and Copilot do not support machine-enforceable permissions. The compiler injects a human-readable permission description into the operational instructions file and prints a warning. + +## Related Skills + +- `/harness-validate` — validate a harness.yaml before compiling +- `/harness-export` — generate a harness.yaml from your current setup +- `/harness-import` — install plugins from a harness.yaml diff --git a/plugins/harness-share/skills/harness-compile/SKILL.md b/plugins/harness-share/skills/harness-compile/SKILL.md new file mode 100644 index 0000000..93c5c81 --- /dev/null +++ b/plugins/harness-share/skills/harness-compile/SKILL.md @@ -0,0 +1,356 @@ +--- +name: harness-compile +description: Use when user invokes /harness-compile or wants to compile a harness.yaml into native config files for Claude Code, Cursor, and GitHub Copilot. Generates CLAUDE.md, AGENT.md, .mcp.json, .cursor/rules/, .vscode/mcp.json, and related files from a single harness.yaml source. Supports --target, --dry-run, and --clean flags. +disable-model-invocation: true +--- + +# Compile a Harness Configuration + +You are compiling a `harness.yaml` file into native configuration files for one or more AI coding tools. Each target tool gets its own idiomatic output: instruction files, MCP server configs, skill files, and permission settings. + +This skill implements the Harness Protocol compiler mapping defined at harnessprotocol.io. The output is deterministic — the same `harness.yaml` always produces the same files. + +## Workflow Order (MANDATORY) + +**Follow these steps in order. Do not skip any step.** + +--- + +### Step 1: Find harness.yaml + +Check for the harness file in this order: +1. A path provided by the user after `/harness-compile` (e.g., `/harness-compile ~/dotfiles/harness.yaml`) +2. `./harness.yaml` in the current directory + +If no file is found at either location, tell the user: +> "No `harness.yaml` found. Specify a path: `/harness-compile path/to/harness.yaml`" + +Read and parse the file. Extract `metadata.name` (use `default` if absent). Extract `version` to confirm v1 format. Note the `instructions.import-mode` value (default: `merge` if not specified). + +If `version` is absent, is the integer `1` (legacy format), or is any value other than the string `"1"`, stop and tell the user: "This harness.yaml uses an unsupported format. Run `/harness-validate` to check the file and see upgrade instructions." + +Also check for any flags the user passed: +- `--target ` — restricts which targets are compiled +- `--dry-run` — preview output without writing any files +- `--clean` — remove orphaned marker blocks in addition to current update +- `--verbose` — show skipped slots and extra detail in the compilation report + +--- + +### Step 2: Platform detection + +If `--target` was provided, skip interactive detection and use those targets directly. `all` means compile for all three: `claude-code`, `cursor`, `copilot`. + +Otherwise, scan the working directory for platform indicators: + +**Claude Code** is present if any of these exist: `CLAUDE.md`, `.claude/`, `.mcp.json` +**Cursor** is present if any of these exist: `.cursor/`, `.cursor/rules/`, `.cursor/mcp.json`, `.cursor/skills/` +**Copilot** is present if any of these exist: `.github/`, `.vscode/mcp.json`, `.github/skills/` + +**Never assume a platform is present.** Detect each independently. If `.github/` is the only Copilot indicator, ask the user: "I found a `.github/` directory but no other Copilot indicators. Are you using GitHub Copilot in this project?" + +Present the detected platforms as a multi-select and ask the user to confirm or adjust: + +> "I detected these targets in your project: +> [x] Claude Code (found: CLAUDE.md, .claude/) +> [x] Cursor (found: .cursor/) +> [ ] Copilot (not detected) +> +> Which targets should I compile for? (Confirm, add, or remove any.)" + +Wait for confirmation before proceeding. If no platforms are detected, ask: +> "No AI tool config directories found. Which targets should I compile for? (claude-code, cursor, copilot)" + +--- + +### Step 3: Compile instructions + +For each confirmed target, write the instruction slots using this mapping: + +| Harness slot | Claude Code | Copilot | Cursor | +|---|---|---|---| +| `instructions.operational` | `CLAUDE.md` | `.github/copilot-instructions.md` | `.cursor/rules/harness.mdc` | +| `instructions.behavioral` | `AGENT.md` | `.github/instructions/behavioral.instructions.md` | `.cursor/rules/behavioral.mdc` | +| `instructions.identity` | `SOUL.md` | (omit — not supported) | (omit — not supported) | + +**Section markers (exact format — do not deviate):** + +Every generated block must be wrapped in markers. The `{name}` value is `metadata.name` from harness.yaml, or `default` if absent. The `{slot}` value is `operational`, `behavioral`, or `identity`. + +``` + +...generated content... + +``` + +Example with `metadata.name: data-engineer` and slot `operational`: +``` + +## Commands +- Build: `dbt run` + +``` + +**Import-mode behavior:** + +- **`merge`** (default): If the target file already exists and contains matching markers, update only the content between those markers. If no markers exist yet, append the marker block at the end of the file (creating the file if it does not exist). +- **`replace`**: Warn the user that existing content will be overwritten and require explicit confirmation before proceeding: + > "Warning: import-mode 'replace' will overwrite `CLAUDE.md`. + > Existing content (42 lines) will be replaced with generated content. + > Your manual customizations outside the markers will be lost. + > + > Proceed? [y/N]" + After confirmation, write the generated content wrapped in markers as the entire file. Skip the confirmation prompt on re-compilation if the file already contains only harness marker blocks. +- **`skip`**: Do not write or modify this slot's file at all. Leave it untouched. + +**Cursor `.mdc` frontmatter (mandatory):** + +Cursor rules files require YAML frontmatter. Always prepend this block before the instruction content for `.cursor/rules/harness.mdc` (operational slot): + +``` +--- +description: Harness operational instructions +globs: **/* +alwaysApply: true +--- +``` + +For `.cursor/rules/behavioral.mdc` (behavioral slot): + +``` +--- +description: Harness behavioral preferences +globs: **/* +alwaysApply: true +--- +``` + +The frontmatter goes before the `` marker block. + +**Copilot `.instructions.md` frontmatter:** + +Copilot instructions files require YAML frontmatter. Always prepend this block: + +``` +--- +applyTo: "**" +--- +``` + +The frontmatter goes before the `` marker block. + +**Directory creation:** Create parent directories before writing if they don't exist (`.cursor/rules/`, `.github/instructions/`). + +--- + +### Step 4: Compile MCP servers + +If the harness has a `mcp-servers:` section, write MCP JSON config files for each confirmed target. + +| Target | File path | +|---|---| +| Claude Code | `.mcp.json` | +| Cursor | `.cursor/mcp.json` | +| Copilot / VS Code | `.vscode/mcp.json` | + +All three files use identical JSON structure. Translate from harness YAML: +- YAML key `transport` → JSON key `type` +- Omit any harness-specific YAML keys that have no MCP JSON equivalent + +```json +{ + "mcpServers": { + "postgres": { + "type": "stdio", + "command": "uvx", + "args": ["mcp-server-postgres", "--connection-string", "${DB_CONNECTION_STRING}"] + } + } +} +``` + +**Note the casing:** `mcpServers` (camelCase) — not `mcp-servers`, not `mcp_servers`. + +**Merging:** If the target MCP file already exists, merge: add new servers defined in the harness, but do not overwrite existing server configurations. Servers already present in the file take precedence. + +If a server name in harness.yaml already exists in the target config file, **do not overwrite** but print a warning (substitute the actual target file path and server name): +``` +Warning: already defines server ''. Existing config kept. + To update it, edit directly or remove the entry and re-run. +``` +For example: `Warning: .cursor/mcp.json already defines server 'postgres'. Existing config kept.` + +Create parent directories if they don't exist. + +--- + +### Step 5: Compile skills + +This step only runs if both conditions are true: +1. The harness has a `plugins:` section +2. At least one plugin has a SKILL.md discoverable via the source lookup below + +For each plugin in `plugins:`, locate its SKILL.md by checking these locations in order: +1. `~/.claude/skills//SKILL.md` — Claude Code global install +2. `.cursor/skills//SKILL.md` — project-local Cursor +3. `.agents/skills//SKILL.md` — agentskills.io shared location + +Use the first SKILL.md found. If no SKILL.md is found in any of these locations, skip that plugin and note it in the compilation report as: `: skipped (no SKILL.md found in ~/.claude/skills/, .cursor/skills/, or .agents/skills/)`. + +For each plugin that has a SKILL.md, copy it to each confirmed target's skill directory: + +| Target | Skill directory | +|---|---| +| Claude Code | (skip — uses plugin install system, not file copy) | +| Cursor | `.cursor/skills//SKILL.md` or `.agents/skills//SKILL.md` | +| Copilot | `.github/skills//SKILL.md` or `.agents/skills//SKILL.md` | + +Ask the user which location to write to if multiple are applicable, or write to the platform-specific location by default (`.cursor/skills//` for Cursor, `.github/skills//` for Copilot). + +**Frontmatter adaptation when copying to Cursor/Copilot:** +- If the source SKILL.md frontmatter has a `dependencies` field, rename it to `compatibility` +- Enforce that the `name` field matches the folder name: lowercase letters and hyphens only, max 64 characters. Truncate and slugify if needed. +- If `description` exceeds 1024 characters, truncate at the last word boundary before 1024 characters and append `…`. + +Create parent directories before writing. + +--- + +### Step 6: Compile permissions + +**Claude Code:** + +If the harness has a `permissions:` section, write or update `.claude/settings.json` with the permissions data: + +```json +{ + "permissions": { + "allow": ["Read", "Glob", "Grep", "Write", "Edit"], + "deny": ["mcp__postgres__drop_*"], + "additionalDirectories": ["sql/", "migrations/"] + } +} +``` + +The keys map as follows: +- `permissions.tools.allow` → `permissions.allow` +- `permissions.tools.deny` → `permissions.deny` +- `permissions.paths` → `permissions.additionalDirectories` + +If `.claude/settings.json` already exists, merge: update or add the `permissions` key without touching other keys in the file. + +**Cursor and Copilot:** + +These tools do not support machine-enforceable permissions via a JSON config. Instead, append a human-readable permission description inside markers to the operational instructions file for that target (e.g., appended to the marker block in `.cursor/rules/harness.mdc` or `.github/copilot-instructions.md`): + +```markdown +## Tool Permissions + +This harness specifies the following tool permissions: +- **Allowed**: Read, Glob, Grep, Write, Edit, Bash +- **Denied**: Any tool matching `mcp__*__drop_*` or `mcp__*__delete_*` + +Please configure your tool's permission settings to match these constraints. +``` + +Print a warning for each non-Claude-Code target that has a `deny` list: +``` +Warning: permissions.tools.deny is not machine-enforceable for target 'cursor'. + Denied tools: mcp__*__drop_* + Instructions have been updated to describe the intended permissions, + but manual tool configuration is required. +``` + +--- + +### Step 7: Handle flags + +**`--dry-run`:** + +Do not write any files. Instead, for each file that would be written, print its path and full content: + +``` +[DRY RUN] Would write: CLAUDE.md +---------------------------------------- + +## Commands +- Build: `dbt run` + +---------------------------------------- + +[DRY RUN] Would write: .mcp.json +---------------------------------------- +{ + "mcpServers": { + "postgres": { ... } + } +} +---------------------------------------- +``` + +End with: +> "Dry run complete. No files were written. Remove --dry-run to apply." + +**`--clean`:** + +In addition to normal compilation, scan all target files for orphaned marker blocks — markers whose `{name}` no longer matches `metadata.name` in the current harness.yaml. + +Example: if a file contains `` but the current harness name is `data-engineer`, that block is orphaned. + +Before deleting any orphaned blocks, the agent **must**: +1. List every orphaned block found: profile name, slot, file, and line numbers +2. Show the user exactly what will be deleted +3. Ask for confirmation: "Remove these N orphaned blocks? [y/N]" +4. Only proceed with deletion if the user confirms + +This confirmation is required every time. `--clean` is a destructive operation with no recovery path — never skip this step. + +--- + +### Step 8: Print compilation report + +After all files are written (or previewed in dry-run mode), print a compilation report: + +``` +Compiled harness: data-engineer (v1.2.0) +Targets: claude-code, cursor + + CLAUDE.md operational merge 48 lines added + AGENT.md behavioral merge 12 lines added + .mcp.json mcp-servers —— 2 servers + .claude/settings.json permissions —— 4 allowed, 1 denied + .cursor/rules/harness.mdc operational merge 48 lines added + .cursor/rules/behavioral.mdc behavioral merge 12 lines added + .cursor/mcp.json mcp-servers —— 2 servers + + Warnings: + permissions.tools.deny is not machine-enforceable for target 'cursor'. +``` + +Format rules: +- Group by target: claude-code entries first, then cursor, then copilot +- Within each target group, list files in slot order: operational, behavioral, identity, mcp-servers, permissions, skills +- Skipped slots are omitted unless `--verbose` is passed +- Show line counts for instruction files where calculable +- Show server counts for MCP files +- Show allowed/denied counts for permissions + +--- + +## Common Mistakes + +| Mistake | Fix | +|---------|-----| +| Using wrong marker format | Markers must be exact: `` — any deviation breaks re-compilation merge logic | +| Using `mcp_servers` or `mcp-servers` in JSON | JSON key must be `mcpServers` (camelCase) | +| Omitting Cursor `.mdc` frontmatter | Always add frontmatter to `.mdc` files — it is mandatory for Cursor to recognize the file | +| Omitting Copilot `.instructions.md` frontmatter | Always add `applyTo: "**"` frontmatter to Copilot instructions files | +| Overwriting existing MCP server configs on merge | Merge means add new servers only; existing server definitions win | +| Silently skipping MCP server key collisions | Always warn when a server name already exists in the target file — never skip silently | +| Expecting `import-mode` to be per-slot | `import-mode` is a single scalar under `instructions:` that applies to all slots. Per-slot override is not supported in v1 | +| Writing SOUL.md for non-Claude-Code targets | `identity` slot is Claude Code-only — omit it for cursor and copilot | +| Assuming platforms are present | Always detect independently; never assume a platform exists | +| Applying `--clean` without the flag | Only remove orphaned markers when `--clean` is explicitly passed | +| Skipping `replace` confirmation | Always confirm with the user before overwriting existing file content | +| Skipping parent directory creation | Always create parent dirs (`.cursor/rules/`, `.github/instructions/`, `.vscode/`) before writing | +| Using `{name}` as literal string in markers | `{name}` must be replaced with the actual `metadata.name` value, or `default` if absent | diff --git a/plugins/harness-share/skills/harness-export/README.md b/plugins/harness-share/skills/harness-export/README.md index a305a68..d1cdf4a 100644 --- a/plugins/harness-share/skills/harness-export/README.md +++ b/plugins/harness-share/skills/harness-export/README.md @@ -10,25 +10,37 @@ Captures your installed harness-kit plugins into a `harness.yaml` file you can s ``` The skill: -1. Reads `~/.claude/skills/` to detect installed plugins -2. Asks which marketplaces you've added -3. Builds entries for each plugin (auto-fills descriptions for known harness-kit plugins) -4. Writes `harness.yaml` to the current directory or a path you specify +1. Scans `~/.claude/skills/`, `.cursor/skills/`, `.github/skills/`, and `.agents/skills/` to detect installed plugins across all AI tools — deduplicating by name +2. Asks which sources the plugins are from and optionally collects a profile name and description +3. Detects harness-generated instruction content in `.cursor/rules/harness.mdc` and `.github/copilot-instructions.md` and offers to include it +4. Detects MCP servers from `.mcp.json`, `.cursor/mcp.json`, and `.vscode/mcp.json` and offers to include them +5. Builds entries for each plugin (auto-fills descriptions for known harness-kit plugins) +6. Writes `harness.yaml` to the current directory or a path you specify ## Output Format ```yaml -version: 1 +$schema: https://harnessprotocol.io/schema/v1/harness.schema.json +version: "1" -marketplaces: - harness-kit: harnessprotocol/harness-kit +metadata: + name: my-harness + description: My personal harness configuration. plugins: - name: explain - marketplace: harness-kit + source: harnessprotocol/harness-kit description: Layered explanations of files, functions, directories, or concepts ``` +## Next Steps + +After exporting, compile to Cursor and Copilot config files: + +``` +/harness-compile +``` + ## Sharing Your Config Commit `harness.yaml` to your dotfiles repo. Teammates can import it with: diff --git a/plugins/harness-share/skills/harness-export/SKILL.md b/plugins/harness-share/skills/harness-export/SKILL.md index 1252408..2bff83c 100644 --- a/plugins/harness-share/skills/harness-export/SKILL.md +++ b/plugins/harness-share/skills/harness-export/SKILL.md @@ -18,13 +18,38 @@ This file follows the **Harness Protocol v1 format** — the open spec at harnes ### Step 1: Detect installed skills -Read the `~/.claude/skills/` directory to find installed skills. Each subdirectory is an installed skill: +Scan all four skill directories. Each subdirectory inside these directories is an installed skill: + +- `~/.claude/skills/` — Claude Code global skills +- `.cursor/skills/` — Cursor project-local skills +- `.github/skills/` — Copilot project-local skills +- `.agents/skills/` — agentskills.io standard shared location ```bash -ls ~/.claude/skills/ +ls ~/.claude/skills/ 2>/dev/null +ls .cursor/skills/ 2>/dev/null +ls .github/skills/ 2>/dev/null +ls .agents/skills/ 2>/dev/null +``` + +Collect the directory names from each location. Deduplicate by skill name across locations — if the same skill name appears in multiple directories, count it once. Track which platforms each skill was found in. + +Show the user the combined result: + ``` +Found skills across AI tools: -Collect the directory names as your list of installed plugin names. + Claude Code (~/.claude/skills/): research, explain, orient + Cursor (.cursor/skills/): research, explain + Copilot (.github/skills/): research + Shared (.agents/skills/): (none) + + Unique skills: research, explain, orient +``` + +Use this deduplicated list as your list of installed plugin names for the steps that follow. + +If all four directories are empty or missing, tell the user: "No installed skills found in any supported location. Nothing to export." and stop. --- @@ -34,21 +59,87 @@ Tell the user what skills you found, then ask: > "I found these installed skills: [list]. A couple of quick questions: > -> 1. For each plugin, which repo is it from? Format: `owner/repo` — for example `siracusa5/harness-kit`. If a plugin is from harness-kit, just say so and I'll fill it in. +> 1. For each plugin, which repo is it from? Format: `owner/repo` — for example `harnessprotocol/harness-kit`. If a plugin is from harness-kit, just say so and I'll fill it in. > 2. What name and description should I give this harness profile? (optional — press enter to skip) > 3. Do you have any MCP servers, env variables, or CLAUDE.md instructions you'd like to include? (optional) > +> Note: MCP server detection is automatic — Step 2.6 will scan `.mcp.json`, `.cursor/mcp.json`, and `.vscode/mcp.json` and ask you separately. You only need to answer about MCP here if you have MCP servers in a non-standard location not covered by those files. +> > If you've only added harness-kit plugins, just say so." Wait for the user's response before proceeding. --- +### Step 2.5: Detect cross-platform instruction content + +Check whether any cross-platform instruction files exist and contain harness-generated marker blocks: + +```bash +grep -l "` and `` marker from those files. +- If the same profile + slot block appears in multiple files with **identical content**, use it once. +- If the same profile + slot block appears in multiple files with **different content**, show the user both versions and ask which to use. +- Store the extracted content to include as the `instructions:` section in harness.yaml (Step 4). + +If the user says **no**, skip — do not include instruction content from cross-platform files. + +If no harness marker blocks are found in any cross-platform file, skip this step silently. + +--- + +### Step 2.6: Detect cross-platform MCP servers + +Scan these three files for MCP server definitions: + +```bash +cat .mcp.json 2>/dev/null +cat .cursor/mcp.json 2>/dev/null +cat .vscode/mcp.json 2>/dev/null +``` + +Each file uses the same JSON structure with a top-level `mcpServers` key. Merge all `mcpServers` entries across all files found. Deduplicate by server name: + +- If the same server name appears in multiple files with the **same config**, note that it is shared and count it once. +- If the same server name appears in multiple files with **different configs**, show the user both configs and ask which to keep. + +Show the user a summary: + +``` +Found MCP servers: + + postgres (in .mcp.json and .cursor/mcp.json — same config) + filesystem (in .mcp.json only) +``` + +> "Would you like to include these in the export? (all / pick / none)" + +If the user selects **all** or **pick** (and picks at least one), store the chosen servers to write to the `mcp-servers:` section in harness.yaml (Step 4). Convert from JSON format back to harness YAML format: JSON key `type` → YAML key `transport`. + +If the user selects **none**, or if no MCP config files are found, skip this step. + +--- + ### Step 3: Build the plugin entries For each installed skill, determine its source repo: -**Known harness-kit plugins** (source: `siracusa5/harness-kit`): +**Known harness-kit plugins** (source: `harnessprotocol/harness-kit`): | Plugin | Description | |--------|-------------| | explain | Layered explanations of files, functions, directories, or concepts | @@ -61,6 +152,10 @@ For each installed skill, determine its source repo: | harness-export | Export your installed plugins to a shareable harness.yaml | | harness-import | Import a harness.yaml and interactively select plugins to install | | harness-validate | Validate a harness.yaml file against the Harness Protocol v1 JSON Schema | +| harness-compile | Compile harness.yaml to native config files for Claude Code, Cursor, and Copilot | +| harness-sync | Sync AI tool configuration across Claude Code, Cursor, and Copilot | +| ship-pr | End-of-task shipping workflow: run tests, open a PR, code review, fix CI, sync base, then squash merge | +| pull-request-sweep | Cross-repo PR sweep: triage all open PRs, run code reviews, merge what's ready, fix quick CI blockers, and report | For any installed skill **not in this table**, ask the user: > "I see `[name]` installed but don't recognize it. What `owner/repo` is it from, and what does it do in one sentence?" @@ -82,7 +177,7 @@ metadata: plugins: - name: explain - source: siracusa5/harness-kit + source: harnessprotocol/harness-kit description: Layered explanations of files, functions, directories, or concepts # additional plugins follow the same structure ``` @@ -119,6 +214,8 @@ Rules: - `version` must be the string `"1"` (quoted), not the integer `1` - `source` is `owner/repo` — no `marketplace:` key, no `marketplaces:` section - Only include `mcp-servers`, `env`, `instructions`, and `permissions` sections if the user provided content for them +- If instruction content was collected in Step 2.5, include it as the `instructions:` section +- If MCP servers were collected in Step 2.6, include them as the `mcp-servers:` section (using YAML `transport:` key, not JSON `type:`) - Omit `metadata` if the user skipped the name/description questions - Do NOT include `harness-export` or `harness-import` in the output unless the user explicitly asks @@ -128,7 +225,9 @@ Rules: Tell the user where the file was written: -> "Saved to `harness.yaml`. Commit it to your dotfiles or share it with a teammate. They can import it with `/harness-import` inside Claude Code, or with the shell fallback: +> "Saved to `harness.yaml`. To compile it to Cursor and Copilot config files, run `/harness-compile`. +> +> To share with teammates: commit it to your dotfiles repo. They can import it with `/harness-import` inside Claude Code, or with the shell fallback: > > ```bash > curl -fsSL https://raw.githubusercontent.com/harnessprotocol/harness-kit/main/harness-restore.sh | bash -s -- harness.yaml @@ -141,7 +240,7 @@ Tell the user where the file was written: | Mistake | Fix | |---------|-----| | Using `version: 1` (integer) | Must be `version: "1"` (string) — this is what distinguishes the protocol format | -| Using `marketplace: harness-kit` | Protocol format uses `source: siracusa5/harness-kit` — no `marketplaces:` section | +| Using `marketplace: harness-kit` | Protocol format uses `source: harnessprotocol/harness-kit` — no `marketplaces:` section | | Including `harness-export` and `harness-import` in the output | Only include plugins the user actually uses | | Writing to a path without confirming | Write to `./harness.yaml` by default. If user specified a path in the invocation, use that | | Adding `mcp-servers:` / `env:` / `instructions:` as empty sections | Only include these sections when the user has actual content to put in them | diff --git a/plugins/harness-share/skills/harness-import/README.md b/plugins/harness-share/skills/harness-import/README.md index ac168a0..d6846ee 100644 --- a/plugins/harness-share/skills/harness-import/README.md +++ b/plugins/harness-share/skills/harness-import/README.md @@ -17,6 +17,8 @@ Without an argument, looks for `harness.yaml` in the current directory. 2. Asks how you want to proceed — all, pick individually, or get more details first 3. Generates the complete Claude Code install command sequence for your selection +If Cursor or GitHub Copilot are detected in the project, it also offers to set them up: copying skill files, writing MCP server configs, and compiling instructions to each tool's native format — all from the same `harness.yaml`. + ## Example Output ``` @@ -27,6 +29,22 @@ Run these in Claude Code: /plugin install research@harness-kit ``` +If other AI tools are detected, a cross-platform setup report is printed after all steps. MCP configs declared in `harness.yaml` are also written to each platform's config file (`.cursor/mcp.json`, `.vscode/mcp.json`): + +``` +Cross-platform setup complete: + + Cursor: + Skills: explain, research (.cursor/skills/) + MCP: postgres (.cursor/mcp.json) + Instructions: harness.mdc (operational), behavioral.mdc (behavioral) + + GitHub Copilot: + Skills: explain, research (.github/skills/) + MCP: (none declared in harness.yaml) + Instructions: copilot-instructions.md (operational) +``` + ## Shell Alternative If you don't have Claude Code, use `harness-restore.sh` instead: diff --git a/plugins/harness-share/skills/harness-import/SKILL.md b/plugins/harness-share/skills/harness-import/SKILL.md index b87fa22..f007a2f 100644 --- a/plugins/harness-share/skills/harness-import/SKILL.md +++ b/plugins/harness-share/skills/harness-import/SKILL.md @@ -1,6 +1,7 @@ --- name: harness-import description: Use when user invokes /harness-import or wants to install plugins from a shared harness.yaml config file. Reads the config, presents each plugin interactively, and generates the Claude Code install commands for the ones the user selects. Handles both Harness Protocol v1 format (version: "1") and the legacy format (version: 1). +disable-model-invocation: true --- # Import a Harness Configuration @@ -39,8 +40,8 @@ Display the plugin list clearly before asking anything: ``` Plugins in this config: - 1. explain (siracusa5/harness-kit) — Layered explanations of files, functions, directories, or concepts - 2. research (siracusa5/harness-kit) — Process any source into a structured, compounding knowledge base + 1. explain (harnessprotocol/harness-kit) — Layered explanations of files, functions, directories, or concepts + 2. research (harnessprotocol/harness-kit) — Process any source into a structured, compounding knowledge base 3. superpowers (obra/superpowers-marketplace) — Structured dev workflows — TDD, systematic debugging, subagent delegation ``` @@ -106,6 +107,42 @@ Close with: --- +### Step 5.5: Offer cross-platform setup + +After generating the Claude Code install commands, scan the current directory for other AI tool indicators. + +**Cursor** is present if any of these exist: `.cursor/`, `.cursor/rules/`, `.cursor/mcp.json`, `.cursor/skills/` +**GitHub Copilot** is present if any of these exist: `.github/`, `.github/skills/`, `.vscode/mcp.json` + +If `.github/` is the only Copilot indicator present, ask the user: "I found a `.github/` directory but no other Copilot indicators. Are you using GitHub Copilot in this project?" Only include Copilot in the multi-select menu if the user confirms. + +If neither platform is detected, skip this step silently — do not ask. + +If one or more platforms are detected, show them and ask which to include: + +> "I detected the following other AI tools in this project: Cursor, GitHub Copilot. Which would you like to set up? +> 1. Both +> 2. Cursor only +> 3. Copilot only +> 4. Neither (skip)" + +For each confirmed platform, copy installed skill SKILL.md files: +- Only copy skills that have an installed SKILL.md at `~/.claude/skills//SKILL.md` +- Only copy skill files for the plugins the user selected in Steps 3–4. Do not copy all installed skills — only the selected subset. +- Cursor: copy to `.cursor/skills//SKILL.md` +- Copilot: copy to `.github/skills//SKILL.md` + +**Frontmatter adaptation when copying:** +- If the source SKILL.md frontmatter has a `dependencies` field, rename it to `compatibility` +- Enforce that the `name` field is lowercase letters and hyphens only, max 64 characters. Truncate and slugify if needed. +- If `description` exceeds 1024 characters, truncate at the last word boundary before 1024 characters and append `…` + +Create parent directories before writing (`.cursor/skills//`, `.github/skills//`, `.github/instructions/`). + +Record which platforms were confirmed — Steps 6 and 8 will use this. + +--- + ### Step 6: Handle MCP servers (if present) If the config has a `mcp-servers:` section, walk through each server: @@ -114,6 +151,35 @@ If the config has a `mcp-servers:` section, walk through each server: If yes, write or update `.mcp.json` in the current directory with the server definition. +If platforms were confirmed in Step 5.5, also write to platform-specific MCP configs: +- Cursor confirmed: also write `.cursor/mcp.json` +- Copilot confirmed: also write `.vscode/mcp.json` + +All three files use the same `mcpServers` JSON structure: + +```json +{ + "mcpServers": { + "postgres": { + "type": "stdio", + "command": "uvx", + "args": ["mcp-server-postgres", "--connection-string", "${DB_CONNECTION_STRING}"] + } + } +} +``` + +**Note the casing:** `mcpServers` (camelCase) — not `mcp-servers`, not `mcp_servers`. + +**Merge behavior (applies to all target files):** If the target file already exists, add new servers but do not overwrite existing server configurations. If a server name already exists in the target file, warn: +``` +Warning: already defines server ''. Existing config kept. + To update it, edit directly or remove the entry and re-run. +``` +For example: `Warning: .cursor/mcp.json already defines server 'postgres'. Existing config kept.` + +Create parent directories before writing (`.cursor/`, `.vscode/`). + If the server command contains `${VAR}` references, note which env vars are needed (the `env:` section will cover them in Step 7). --- @@ -145,6 +211,65 @@ If the config has an `instructions:` section and `import-mode: merge` (or if imp If yes, append to the file the user specifies. If `import-mode: replace` is set, warn: > "This config requests full replacement of existing instructions. That will overwrite your current CLAUDE.md/AGENT.md. Are you sure?" +If Cursor or Copilot was confirmed in Step 5.5, also compile instructions to those platforms using the same `import-mode` from `harness.yaml`. Use `metadata.name` from the harness.yaml as `{name}` (or `default` if absent). Wrap all generated content in section markers (exact format — do not deviate): + +``` + +...generated content... + +``` + +**Cursor operational** — write `.cursor/rules/harness.mdc` with mandatory frontmatter before the marker block: + +``` +--- +description: Harness operational instructions +globs: **/* +alwaysApply: true +--- +``` + +**Cursor behavioral** — if `instructions.behavioral` is present in harness.yaml, write `.cursor/rules/behavioral.mdc` with mandatory frontmatter before the marker block: + +``` +--- +description: Harness behavioral preferences +globs: **/* +alwaysApply: true +--- +``` + +Then content from `instructions.behavioral` in section markers `` / ``. + +**GitHub Copilot operational** — write `.github/copilot-instructions.md` with mandatory frontmatter before the marker block: + +``` +--- +applyTo: "**" +--- +``` + +**GitHub Copilot behavioral** — if `instructions.behavioral` is present in harness.yaml, write `.github/instructions/behavioral.instructions.md` with mandatory frontmatter before the marker block: + +``` +--- +applyTo: "**" +--- +``` + +Then content from `instructions.behavioral` in section markers `` / ``. + +**Identity slot:** Omit for both Cursor and Copilot — the `identity` slot is Claude Code-only. + +Apply the same import-mode behavior for all Cursor and Copilot files: +- **`merge`** (default): If the file exists and contains matching markers, update the content between them. If no markers exist yet, append the marker block at the end (creating the file if it does not exist). +- **`replace`**: Before overwriting any Cursor or Copilot instruction file, warn the user and require confirmation, the same as for CLAUDE.md: + > "This config requests full replacement of existing [file]. That will overwrite your current [file] content. Are you sure?" + Never apply `replace` without confirmation. +- **`skip`**: Do not write or modify the file. + +Create parent directories before writing (`.cursor/rules/`, `.github/`, `.github/instructions/`). + --- ### Step 9: Offer shell fallback @@ -159,6 +284,28 @@ After all the above, add: --- +### Step 9.5: Cross-platform compilation report + +If cross-platform setup was performed (at least one platform confirmed in Step 5.5), print a summary after all steps complete: + +``` +Cross-platform setup complete: + + Cursor: + Skills: explain, research (.cursor/skills/) + MCP: postgres (.cursor/mcp.json) + Instructions: harness.mdc (operational), behavioral.mdc (behavioral) + + GitHub Copilot: + Skills: explain, research (.github/skills/) + MCP: (none declared in harness.yaml) + Instructions: copilot-instructions.md (operational), behavioral.instructions.md (behavioral) +``` + +Omit any row where nothing was written for that category (e.g., omit `MCP:` if the harness has no `mcp-servers:` section). Omit a platform block entirely if that platform was not confirmed or nothing was written for it. If cross-platform setup was skipped (Step 5.5 produced no confirmations), do not print this report. + +--- + ## Common Mistakes | Mistake | Fix | @@ -168,3 +315,12 @@ After all the above, add: | Skipping the marketplace add commands | They must come before the plugin installs, or the installs will fail | | Ignoring `mcp-servers`, `env`, `instructions` sections | Always check for these sections and handle them in the appropriate steps | | Prompting the user for sensitive env var values | Never ask for secret values — just tell the user which vars to set and where | +| Asking about cross-platform setup when no other platforms are detected | Only prompt for cross-platform setup if Cursor or Copilot indicators are found; skip silently otherwise | +| Copying skill SKILL.md files that aren't installed | Only copy skills that have a SKILL.md at `~/.claude/skills//SKILL.md` | +| Using `mcp_servers` or `mcp-servers` in MCP JSON files | JSON key must be `mcpServers` (camelCase) in all target files | +| Silently skipping MCP server key collisions in Cursor/Copilot configs | Always warn when a server name already exists in the target file — never skip silently | +| Omitting Cursor `.mdc` frontmatter | Always add `description`, `globs`, `alwaysApply` frontmatter — it is mandatory for Cursor to recognize the file | +| Omitting Copilot `copilot-instructions.md` frontmatter | Always add `applyTo: "**"` frontmatter to Copilot instructions files | +| Using wrong section marker format | Markers must be exact: `` — any deviation breaks merge logic | +| Skipping parent directory creation | Always create parent dirs (`.cursor/skills//`, `.github/skills//`, `.cursor/rules/`, `.vscode/`) before writing | +| Printing the cross-platform report when nothing was set up | Only print Step 9.5 report if at least one platform was confirmed in Step 5.5 | diff --git a/plugins/harness-share/skills/harness-sync/README.md b/plugins/harness-share/skills/harness-sync/README.md new file mode 100644 index 0000000..027b026 --- /dev/null +++ b/plugins/harness-share/skills/harness-sync/README.md @@ -0,0 +1,112 @@ +# harness-sync + +Syncs AI tool configurations bidirectionally across Claude Code, Cursor, and GitHub Copilot. Inventories installed skills, instruction blocks, and MCP server configs on each detected platform, reports what's out of sync, and lets you push or pull changes. + +## Usage + +``` +/harness-sync +/harness-sync --target cursor +``` + +Without a flag, detects all platforms present in the project and compares them. + +## What It Does + +1. Detects which AI tools are configured in the project (Claude Code, Cursor, Copilot) +2. Inventories skills, MCP configs, and harness instruction blocks on each platform +3. Reports divergence — what's missing, what differs, what's in sync +4. Asks which sync direction you want (push or pull) +5. Detects and surfaces conflicts — shows both versions side by side, never overwrites silently +6. Executes the sync, touching only divergent items +7. Re-scans to verify platforms are now in sync + +## Flags + +| Flag | Description | +|------|-------------| +| `--target ` | Restrict scope to one platform: `claude-code`, `cursor`, or `copilot`. Useful when you only want to push from one platform to another specific one, ignoring unrelated platforms. All platforms are still inventoried for the divergence report. | + +## Divergence Report + +After inventorying each platform, you'll see a report like: + +``` +Divergence detected: + +Skills: + ✓ research — all platforms + ✓ explain — all platforms + ✗ orient — Claude Code only (missing from Cursor, Copilot) + +MCP: + △ postgres — Claude Code, Cursor (missing from Copilot) + ✗ filesystem — Claude Code only + +Instructions: + CLAUDE.md ↔ .cursor/rules/harness.mdc: content differs (my-harness:operational block) +``` + +## Sync Directions + +**Push** — pick one platform as the source of truth, write its content to the others. Uses harness-compile logic for consistent output (marker blocks, frontmatter, MCP merging). + +**Pull** — read native changes from Cursor or Copilot config files, merge them back into `harness.yaml`, then recompile everything. This modifies your `harness.yaml` source of truth — you'll be asked to confirm before any changes are written. + +## Conflict Handling + +If the same harness block was modified independently on two platforms, both versions are shown side by side: + +``` +Conflict: my-harness:operational + +Claude Code (CLAUDE.md): + [content A] + +Cursor (.cursor/rules/harness.mdc): + [content B] + +How would you like to resolve this? +1. Use Claude Code version (overwrite Cursor) +2. Use Cursor version (overwrite Claude Code) +3. Edit manually first, then re-run /harness-sync +4. Skip this conflict for now +``` + +Conflicts are never silently resolved. + +## Skill Inventory + +Skills are tracked by install location and labelled accordingly: + +| Location | Tag | Notes | +|----------|-----|-------| +| `~/.claude/skills/` | `(global)` | Claude Code global install via plugin marketplace | +| `.cursor/skills/` | `(local)` | Cursor project-local skill file | +| `~/.cursor/skills/` | `(global)` | Cursor global skills (if directory exists) | +| `.github/skills/` | `(local)` | Copilot project-local skill file | +| `.agents/skills/` | `(shared)` | agentskills.io shared location (Cursor + Copilot) | + +Global and project-local installs are not interchangeable — the sync report distinguishes them. + +## Sync Summary + +After execution you'll see what changed: + +``` +Sync complete: + + Added: .cursor/skills/orient/SKILL.md + Updated: .cursor/mcp.json (added filesystem server) + Updated: .cursor/rules/harness.mdc (my-harness:operational block) + Skipped: .github/ (Copilot not detected) + +All platforms are now in sync. +``` + +## Related Skills + +- `/harness-compile` — compile a harness.yaml into native configs (push uses harness-compile logic) +- `/harness-validate` — validate a harness.yaml before syncing +- `/harness-export` — capture your current setup into a harness.yaml +- `/harness-import` — install plugins from a harness.yaml diff --git a/plugins/harness-share/skills/harness-sync/SKILL.md b/plugins/harness-share/skills/harness-sync/SKILL.md new file mode 100644 index 0000000..9138e83 --- /dev/null +++ b/plugins/harness-share/skills/harness-sync/SKILL.md @@ -0,0 +1,304 @@ +--- +name: harness-sync +description: Use when user invokes /harness-sync or wants to sync AI tool configurations across Claude Code, Cursor, and GitHub Copilot. Inventories installed skills, instruction blocks, and MCP configs on all detected platforms, reports divergence, and lets the user push or pull changes. Push path reuses harness-compile logic. Pull path updates harness.yaml and recompiles. Never overwrites without explicit confirmation. +disable-model-invocation: true +--- + +# Sync Harness Configurations Across Platforms + +You are syncing AI tool configurations across Claude Code, Cursor, and GitHub Copilot. You will inventory what is installed and configured on each platform, report any divergence, and execute the sync direction the user chooses. + +This skill depends on harness-compile: the push path reuses harness-compile's compilation logic to write the selected source platform's content to all target platforms. + +## Workflow Order (MANDATORY) + +**Follow these steps in order. Do not skip any step.** + +--- + +### Step 1: Detect platforms + +Check for any `--target ` flag the user passed. If present, restrict the entire workflow to that platform only. Valid values: `claude-code`, `cursor`, `copilot`. For sync, `--target ` means "treat this platform as the only target for push output." It does not restrict which platforms are scanned in Step 2 — all detected platforms are still inventoried so divergence can be identified. + +Otherwise, scan the working directory for platform indicators: + +**Claude Code** is present if any of these exist: `CLAUDE.md`, `.claude/`, `.mcp.json` +**Cursor** is present if any of these exist: `.cursor/`, `.cursor/rules/`, `.cursor/mcp.json`, `.cursor/skills/` +**Copilot** is present if any of these exist: `.github/`, `.vscode/mcp.json`, `.github/skills/` + +**Never assume a platform is present.** Detect each independently. If `.github/` is the only Copilot indicator, ask the user: +> "I found a `.github/` directory but no other Copilot indicators. Are you using GitHub Copilot in this project?" + +Also check global skill directories: +- `~/.claude/skills/` — Claude Code global skill installs +- `~/.cursor/skills/` — Cursor global skills (if the directory exists) + +If no platforms are detected, tell the user: +> "No AI tool config directories found. Which platforms are you using? (claude-code, cursor, copilot)" + +--- + +### Step 2: Inventory current state + +For each detected platform, catalog three things: + +**Installed skills:** +- Claude Code: `~/.claude/skills/` (global) and any project-local skill symlinks +- Cursor: `.cursor/skills/` (project-local), `~/.cursor/skills/` (global if the directory exists) +- Copilot: `.github/skills/` (project-local) +- Shared: `.agents/skills/` (agentskills.io shared — available to both Cursor and Copilot) + +Distinguish global vs. project-local installs with a `(global)` or `(local)` tag. Label skills found in `.agents/skills/` as `(shared)` in the inventory display. + +**Instruction files:** Read each platform's instruction files and identify: +- Harness marker blocks: lines matching `` +- Manual content: any content outside marker blocks + +Report marker blocks by their `{name}:{slot}` identifier. + +**MCP server configs:** Parse JSON from: +- Claude Code: `.mcp.json` +- Cursor: `.cursor/mcp.json` +- Copilot / VS Code: `.vscode/mcp.json` + +List each server by name and transport type. + +Present a unified inventory: + +``` +Current state: + +Claude Code: + Skills: research (global), explain (global), orient (global) + MCP: postgres (stdio), filesystem (stdio) + Harness blocks in CLAUDE.md: [my-harness:operational], [my-harness:behavioral] + +Cursor: + Skills: research (local), explain (local) + MCP: postgres (stdio) + Harness blocks in .cursor/rules/harness.mdc: [my-harness:operational] + +Copilot: + Skills: (none) + MCP: (none) + Harness blocks in .github/copilot-instructions.md: (none) +``` + +If a platform has no instruction file for a slot, show `(none)` for that slot's harness blocks. + +--- + +### Step 3: Detect divergence + +Compare across all detected platforms: + +**Skills divergence:** For each unique skill name found anywhere, note which platforms have it and which don't. + +**MCP divergence:** For each unique MCP server name found anywhere, note which platforms have it and which don't. If a server exists on multiple platforms but with different configurations (different command, args, or type), flag it as a configuration conflict. + +**Instructions divergence:** For each unique harness block identifier (e.g., `my-harness:operational`) found anywhere, compare the content between platforms. If the content differs between any two platforms, flag it. + +Present a divergence report: + +``` +Divergence detected: + +Skills: + ✓ research — all platforms + ✓ explain — all platforms + ✗ orient — Claude Code only (missing from Cursor, Copilot) + +MCP: + △ postgres — Claude Code, Cursor (missing from Copilot) + ✗ filesystem — Claude Code only + +Instructions: + CLAUDE.md ↔ .cursor/rules/harness.mdc: content differs (my-harness:operational block) + .github/copilot-instructions.md: no harness blocks (Claude Code has my-harness:operational, my-harness:behavioral) +``` + +If no divergence is found across any dimension, tell the user: +> "All platforms are in sync. Nothing to do." + +And stop here. + +--- + +### Step 4: Propose sync direction + +Ask the user: + +> "How would you like to sync? +> 1. Push from Claude Code → other platforms +> 2. Push from Cursor → other platforms +> 3. Pull changes from Cursor/Copilot into harness.yaml, then recompile +> 4. Show me the diff first +> +> Which would you like?" + +Wait for the user's answer before proceeding. + +> Note: Copilot cannot be a push source. To incorporate changes made directly in Copilot config files into harness.yaml, use option 3 (pull). + +**Option 1 or 2 (push):** Note the source platform. Ask which targets to push to: +> "Push to which platforms? (all detected platforms, or list specific ones)" + +Wait for target confirmation before proceeding to Step 5. + +**Option 3 (pull):** Note that the user wants to pull external changes back into harness.yaml and proceed to Step 5. + +**Option 4 (diff):** Show a detailed diff of each divergent section: +- For skills: list which skills are missing from which platforms +- For MCP: list which servers are missing or differ between platforms, showing the full server config side by side if configs differ +- For instructions: show the full content of each divergent harness block from each platform, labelled by platform and file path + +After showing all diffs, return to this question. + +--- + +### Step 5: Conflict detection + +Before executing any sync, check for conflicts: a conflict exists when the same harness block identifier (e.g., `my-harness:operational`) has different content on two or more platforms that the user is treating as peers (i.e., neither is designated the authoritative source). + +**For push operations:** The source platform is authoritative. There are no conflicts — the source wins. Skip to Step 6. + +**For pull operations:** Two kinds of conflicts must be resolved before writing to harness.yaml: + +First, if multiple external platforms are selected as pull sources (e.g., both Cursor and Copilot), compare those platforms against each other. If the same harness block exists on both with different content, that is a cross-platform conflict that must be surfaced before either is merged into harness.yaml: + +``` +Cross-platform conflict: my-harness:operational + +Cursor (.cursor/rules/harness.mdc): + [content A] + +Copilot (.github/copilot-instructions.md): + [content B] + +These platforms disagree. Which should I pull from? +1. Use Cursor version +2. Use Copilot version +3. Edit manually first, then re-run /harness-sync +4. Skip this block +``` + +Resolve all cross-platform conflicts first, then proceed to compare the surviving version against harness.yaml. + +Second, any harness block that exists on the pull source and in harness.yaml with different content is a conflict. + +For each conflict, show both versions side by side and ask for resolution: + +``` +Conflict: my-harness:operational + +Claude Code (CLAUDE.md): + [full content of the block from CLAUDE.md] + +Cursor (.cursor/rules/harness.mdc): + [full content of the block from .cursor/rules/harness.mdc] + +How would you like to resolve this? +1. Use Claude Code version (overwrite Cursor) +2. Use Cursor version (overwrite Claude Code) +3. Edit manually first, then re-run /harness-sync +4. Skip this conflict for now +``` + +**Never silently overwrite.** Always show both versions. Always wait for the user's resolution choice before proceeding. Resolve all conflicts before moving to Step 6. + +If the user chooses option 3 (edit manually), stop and tell them: +> "Make your edits, then run `/harness-sync` again to pick up where we left off." + +If the user chooses option 4 (skip), note the skipped conflict and continue resolving remaining conflicts. Skipped conflicts will not be synced. + +--- + +### Step 6: Execute + +**Push path:** + +Use harness-compile logic to write the source platform's content to all confirmed target platforms. Only update items that are divergent — do not touch content that is already in sync across platforms. + +When reading skill SKILL.md files for the push source, locate them as follows: +- **Push source = Claude Code**: read from `~/.claude/skills//SKILL.md` +- **Push source = Cursor**: check `.cursor/skills//SKILL.md` first; if not found, fall back to `~/.cursor/skills//SKILL.md` +- Skills in `.agents/skills//SKILL.md` are available to both platforms and may be used as a source if neither platform-specific path yields a SKILL.md + +For each divergent item: +- **Skills**: copy the skill's SKILL.md (located via the lookup above) to each target platform's skill directory (`.cursor/skills//SKILL.md` for Cursor, `.github/skills//SKILL.md` for Copilot). Apply frontmatter adaptation rules from harness-compile (rename `dependencies` to `compatibility`, enforce name constraints, truncate description if over 1024 characters). +- **MCP servers**: add missing servers to target MCP config files using the source platform's server definition. Do not overwrite existing server entries in the target — if a server name already exists in the target file, print a warning and skip it. +- **Instructions**: write the source platform's harness block content into each target platform's instruction file, following the slot mapping and marker format from harness-compile. Follow import-mode rules: if the target file already contains markers for that block, update between the markers; if no markers exist yet, append the block. + +Create parent directories before writing if they don't exist. + +**Pull path:** + +1. Read all content that exists outside harness marker blocks in the source platform's instruction files (Cursor or Copilot). This is native content the user added directly. +2. Read all MCP servers that exist in source platform config files but are not in harness.yaml. + +**Skill files are not pulled.** Skills in harness.yaml are plugin source references (`source: owner/repo`), not file content. If Cursor has a skill in `.cursor/skills/` that is not in harness.yaml, it cannot be automatically added — the `plugins:` section requires knowing the plugin's source repo. Tell the user: +> "I found skills in `.cursor/skills/` not in your harness.yaml: [list]. To add these, run `/harness-export` and specify their source repos when prompted." + +3. Summarize what would be added to harness.yaml: + +``` +Changes to write to harness.yaml: + + instructions.operational: 14 lines of content from .cursor/rules/harness.mdc (outside harness blocks) + mcp-servers: add "redis" server from .cursor/mcp.json +``` + +4. Warn the user: +> "This will modify your harness.yaml — your source of truth file. The changes above will be merged in." +> +> "Proceed? [y/N]" + +5. If the user confirms, write the updated harness.yaml with the pulled content merged in. +6. After writing harness.yaml, immediately run harness-compile logic to recompile all platforms from the updated harness.yaml, applying the new content consistently everywhere. + +--- + +### Step 7: Verify + +Re-run the Step 2 inventory scan across all platforms that were modified. Compare the results to confirm platforms are now in sync. + +Report the outcome: + +``` +Sync complete: + + Added: .cursor/skills/orient/SKILL.md + Updated: .cursor/mcp.json (added filesystem server) + Updated: .cursor/rules/harness.mdc (my-harness:operational block) + Skipped: .github/ (Copilot not detected) + +All platforms are now in sync. +``` + +If any divergence remains (e.g., a skipped conflict, a server that already existed in the target), report it: + +``` +Remaining divergence: + my-harness:operational block was skipped (conflict unresolved) + Run /harness-sync again to address it. +``` + +--- + +## Common Mistakes + +| Mistake | Fix | +|---------|-----| +| Silently overwriting divergent content | Always show the conflict and ask — never write without the user's explicit choice | +| Treating `.github/` alone as Copilot confirmation | Ask the user if `.github/` is the only indicator — it may exist for CI only | +| Conflating global and project-local skills | Always label skills as `(global)` or `(local)` in the inventory — they are not equivalent | +| Skipping conflict detection on push | Push makes the source authoritative — but still check whether the user confirmed the target list before writing | +| Overwriting existing MCP server entries in targets | On push, add missing servers only — warn and skip if a server name already exists in the target | +| Running pull without warning about harness.yaml modification | Always warn that pull modifies harness.yaml before writing | +| Forgetting to recompile after pull | Pull must update harness.yaml and then run harness-compile logic — the two steps are always paired | +| Skipping Step 7 verification | Always re-scan after sync to confirm platforms are in sync and report any remaining divergence | +| Reporting all items in the sync report, not just changed ones | Only report files that were actually added, updated, or skipped — do not list unchanged files | +| Applying frontmatter adaptation only for new skills | Frontmatter rules (rename `dependencies`, enforce name constraints, truncate description) apply on every copy to Cursor/Copilot, including updates | +| Showing only one version in a conflict | Always show both versions side by side with platform labels — never show just one | +| Skipping cross-platform comparison on pull | When multiple platforms are pull sources, compare them against each other before merging into harness.yaml | +| Expecting Copilot to be a push source | Copilot is a pull-only platform. Use option 3 to bring Copilot changes back into harness.yaml | diff --git a/plugins/harness-share/skills/harness-validate/README.md b/plugins/harness-share/skills/harness-validate/README.md new file mode 100644 index 0000000..0375444 --- /dev/null +++ b/plugins/harness-share/skills/harness-validate/README.md @@ -0,0 +1,38 @@ +# harness-validate + +Validates a `harness.yaml` file against the Harness Protocol v1 JSON Schema. Reports errors with field paths and fix suggestions. + +## Usage + +``` +/harness-validate +/harness-validate path/to/harness.yaml +``` + +Without an argument, looks for `harness.yaml` in the current directory. + +## What It Does + +1. Finds the harness file (argument path or `./harness.yaml`) +2. Installs validation dependencies in a temporary venv (`jsonschema`, `pyyaml`) +3. Fetches the Harness Protocol v1 JSON Schema from GitHub +4. Runs full schema validation against the file +5. Reports PASS/FAIL with field paths and suggested fixes + +If the schema cannot be fetched, falls back to basic offline checks (version, metadata.name). + +## Common Errors + +| Error | Fix | +|-------|-----| +| `version` must be string `"1"` | Change `version: 1` to `version: "1"` | +| `source` is not a valid property | Replace `marketplace: key` with `source: owner/repo` | +| `default` not allowed when `sensitive: true` | Remove the `default` value | +| `metadata.name` is required | Add a `metadata.name` field | +| Unknown additional property | Check for typos in field names | + +## When to Use + +- Before sharing a `harness.yaml` with others +- After manually editing a harness file +- Before running `/harness-compile` on an untested config diff --git a/plugins/harness-share/skills/harness-validate/SKILL.md b/plugins/harness-share/skills/harness-validate/SKILL.md index 52a5f0d..8988067 100644 --- a/plugins/harness-share/skills/harness-validate/SKILL.md +++ b/plugins/harness-share/skills/harness-validate/SKILL.md @@ -1,6 +1,7 @@ --- name: harness-validate description: Use when user invokes /harness-validate or wants to check whether a harness.yaml file is valid according to the Harness Protocol v1 JSON Schema. Reports validation errors with field paths and helpful fix suggestions. +disable-model-invocation: true --- # Validate a Harness Configuration @@ -48,7 +49,7 @@ except ImportError: print("ERROR: jsonschema not installed") sys.exit(1) -SCHEMA_URL = "https://raw.githubusercontent.com/siracusa5/harness-protocol/spec/v1-foundation/schema/draft/harness.schema.json" +SCHEMA_URL = "https://raw.githubusercontent.com/harnessprotocol/harness-protocol/spec/v1-foundation/schema/draft/harness.schema.json" HARNESS_FILE = "harness.yaml" # replace with actual path # Fetch the schema @@ -118,7 +119,7 @@ Display errors with clear field paths and fix suggestions: plugins → 0 → source: 'marketplace' is not a valid property Fix: Use source: owner/repo instead of marketplace: key. - Example: source: siracusa5/harness-kit + Example: source: harnessprotocol/harness-kit env → 0: 'default' is not allowed when sensitive is true Fix: Remove the default value — sensitive vars must be set by the user, diff --git a/plugins/orient/.claude-plugin/plugin.json b/plugins/orient/.claude-plugin/plugin.json index b76a086..acdbf38 100644 --- a/plugins/orient/.claude-plugin/plugin.json +++ b/plugins/orient/.claude-plugin/plugin.json @@ -1,5 +1,7 @@ { "name": "orient", "description": "Topic-focused session orientation — search graph, knowledge, journal, and research for a specific topic", - "version": "0.2.0" + "version": "0.2.0", + "category": "research-knowledge", + "tags": ["orientation", "knowledge-graph", "session", "topic-search"] } diff --git a/plugins/pull-request-sweep/.claude-plugin/plugin.json b/plugins/pull-request-sweep/.claude-plugin/plugin.json index 16a5185..4821b99 100644 --- a/plugins/pull-request-sweep/.claude-plugin/plugin.json +++ b/plugins/pull-request-sweep/.claude-plugin/plugin.json @@ -2,6 +2,8 @@ "name": "pull-request-sweep", "description": "Cross-repo PR sweep: triage all open PRs, run code reviews, merge what's ready, fix quick CI blockers", "version": "0.1.0", + "category": "devops", + "tags": ["pull-request", "triage", "ci-cd", "code-review", "multi-repo"], "requires": { "env": [ { diff --git a/plugins/research/.claude-plugin/plugin.json b/plugins/research/.claude-plugin/plugin.json index f189a55..31b3ea5 100644 --- a/plugins/research/.claude-plugin/plugin.json +++ b/plugins/research/.claude-plugin/plugin.json @@ -2,6 +2,8 @@ "name": "research", "description": "Process any source into a structured, compounding knowledge base", "version": "0.3.0", + "category": "research-knowledge", + "tags": ["research", "knowledge-base", "web-scraping", "pdf", "github", "sources"], "requires": { "env": [ { diff --git a/plugins/review/.claude-plugin/plugin.json b/plugins/review/.claude-plugin/plugin.json index 90f83a8..a92c23b 100644 --- a/plugins/review/.claude-plugin/plugin.json +++ b/plugins/review/.claude-plugin/plugin.json @@ -2,6 +2,8 @@ "name": "review", "description": "Code review for a branch, PR, or path — structured output with severity labels and cross-file analysis", "version": "0.3.0", + "category": "code-quality", + "tags": ["code-review", "pull-request", "static-analysis", "github"], "developed_with": "claude-sonnet-4", "requires": { "env": [ diff --git a/plugins/ship-pr/.claude-plugin/plugin.json b/plugins/ship-pr/.claude-plugin/plugin.json index a5e1b68..553ff9f 100644 --- a/plugins/ship-pr/.claude-plugin/plugin.json +++ b/plugins/ship-pr/.claude-plugin/plugin.json @@ -2,6 +2,8 @@ "name": "ship-pr", "description": "End-of-task workflow: run tests, open a PR, code review, fix CI, sync base, then squash merge", "version": "0.1.0", + "category": "devops", + "tags": ["pull-request", "ci-cd", "merge", "testing", "workflow"], "requires": { "env": [ { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..dd5ed84 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,4007 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: {} + + marketplace: + dependencies: + '@harness-kit/shared': + specifier: workspace:* + version: link:../packages/shared + '@supabase/supabase-js': + specifier: ^2.0.0 + version: 2.99.1 + next: + specifier: ^15.0.0 + version: 15.5.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: + specifier: ^19.0.0 + version: 19.2.4 + react-dom: + specifier: ^19.0.0 + version: 19.2.4(react@19.2.4) + devDependencies: + '@tailwindcss/postcss': + specifier: ^4.0.0 + version: 4.2.1 + '@types/node': + specifier: ^22.0.0 + version: 22.19.15 + '@types/react': + specifier: ^19.0.0 + version: 19.2.14 + '@types/react-dom': + specifier: ^19.0.0 + version: 19.2.3(@types/react@19.2.14) + postcss: + specifier: ^8.0.0 + version: 8.5.8 + tailwindcss: + specifier: ^4.0.0 + version: 4.2.1 + typescript: + specifier: ^5.0.0 + version: 5.9.3 + + packages/shared: + devDependencies: + typescript: + specifier: ^5.0.0 + version: 5.9.3 + + website: + dependencies: + fumadocs-core: + specifier: ^15.0.0 + version: 15.8.5(@types/react@19.2.14)(next@15.5.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + fumadocs-mdx: + specifier: ^11.0.0 + version: 11.10.1(fumadocs-core@15.8.5(@types/react@19.2.14)(next@15.5.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(next@15.5.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) + fumadocs-ui: + specifier: ^15.0.0 + version: 15.8.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(next@15.5.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tailwindcss@4.2.1) + mdast-util-from-markdown: + specifier: ^2.0.3 + version: 2.0.3 + next: + specifier: ^15.0.0 + version: 15.5.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: + specifier: ^19.0.0 + version: 19.2.4 + react-dom: + specifier: ^19.0.0 + version: 19.2.4(react@19.2.4) + unist-util-visit: + specifier: ^5.1.0 + version: 5.1.0 + devDependencies: + '@tailwindcss/postcss': + specifier: ^4.0.0 + version: 4.2.1 + '@types/node': + specifier: ^22.0.0 + version: 22.19.15 + '@types/react': + specifier: ^19.0.0 + version: 19.2.14 + '@types/react-dom': + specifier: ^19.0.0 + version: 19.2.3(@types/react@19.2.14) + postcss: + specifier: ^8.0.0 + version: 8.5.8 + tailwindcss: + specifier: ^4.0.0 + version: 4.2.1 + typescript: + specifier: ^5.0.0 + version: 5.9.3 + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@emnapi/runtime@1.9.0': + resolution: {integrity: sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==} + + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@floating-ui/core@1.7.5': + resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} + + '@floating-ui/dom@1.7.6': + resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} + + '@floating-ui/react-dom@2.1.8': + resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.11': + resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + + '@formatjs/intl-localematcher@0.6.2': + resolution: {integrity: sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==} + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@mdx-js/mdx@3.1.1': + resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} + + '@next/env@15.5.12': + resolution: {integrity: sha512-pUvdJN1on574wQHjaBfNGDt9Mz5utDSZFsIIQkMzPgNS8ZvT4H2mwOrOIClwsQOb6EGx5M76/CZr6G8i6pSpLg==} + + '@next/swc-darwin-arm64@15.5.12': + resolution: {integrity: sha512-RnRjBtH8S8eXCpUNkQ+543DUc7ys8y15VxmFU9HRqlo9BG3CcBUiwNtF8SNoi2xvGCVJq1vl2yYq+3oISBS0Zg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@15.5.12': + resolution: {integrity: sha512-nqa9/7iQlboF1EFtNhWxQA0rQstmYRSBGxSM6g3GxvxHxcoeqVXfGNr9stJOme674m2V7r4E3+jEhhGvSQhJRA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@15.5.12': + resolution: {integrity: sha512-dCzAjqhDHwmoB2M4eYfVKqXs99QdQxNQVpftvP1eGVppamXh/OkDAwV737Zr0KPXEqRUMN4uCjh6mjO+XtF3Mw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@next/swc-linux-arm64-musl@15.5.12': + resolution: {integrity: sha512-+fpGWvQiITgf7PUtbWY1H7qUSnBZsPPLyyq03QuAKpVoTy/QUx1JptEDTQMVvQhvizCEuNLEeghrQUyXQOekuw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@next/swc-linux-x64-gnu@15.5.12': + resolution: {integrity: sha512-jSLvgdRRL/hrFAPqEjJf1fFguC719kmcptjNVDJl26BnJIpjL3KH5h6mzR4mAweociLQaqvt4UyzfbFjgAdDcw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@next/swc-linux-x64-musl@15.5.12': + resolution: {integrity: sha512-/uaF0WfmYqQgLfPmN6BvULwxY0dufI2mlN2JbOKqqceZh1G4hjREyi7pg03zjfyS6eqNemHAZPSoP84x17vo6w==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@next/swc-win32-arm64-msvc@15.5.12': + resolution: {integrity: sha512-xhsL1OvQSfGmlL5RbOmU+FV120urrgFpYLq+6U8C6KIym32gZT6XF/SDE92jKzzlPWskkbjOKCpqk5m4i8PEfg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@15.5.12': + resolution: {integrity: sha512-Z1Dh6lhFkxvBDH1FoW6OU/L6prYwPSlwjLiZkExIAh8fbP6iI/M7iGTQAJPYJ9YFlWobCZ1PHbchFhFYb2ADkw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@orama/orama@3.1.18': + resolution: {integrity: sha512-a61ljmRVVyG5MC/698C8/FfFDw5a8LOIvyOLW5fztgUXqUpc1jOfQzOitSCbge657OgXXThmY3Tk8fpiDb4UcA==} + engines: {node: '>= 20.0.0'} + + '@radix-ui/number@1.1.1': + resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} + + '@radix-ui/primitive@1.1.3': + resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} + + '@radix-ui/react-accordion@1.2.12': + resolution: {integrity: sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-arrow@1.1.7': + resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collapsible@1.1.12': + resolution: {integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collection@1.1.7': + resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dialog@1.1.15': + resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-direction@1.1.1': + resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.11': + resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.3': + resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.7': + resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-id@1.1.1': + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-navigation-menu@1.2.14': + resolution: {integrity: sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popover@1.1.15': + resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popper@1.2.8': + resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.9': + resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.5': + resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-roving-focus@1.1.11': + resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-scroll-area@1.2.10': + resolution: {integrity: sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-slot@1.2.4': + resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-tabs@1.1.13': + resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.1': + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.1': + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-previous@1.1.1': + resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.1': + resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.1': + resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.2.3': + resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/rect@1.1.1': + resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + + '@shikijs/core@3.23.0': + resolution: {integrity: sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==} + + '@shikijs/engine-javascript@3.23.0': + resolution: {integrity: sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==} + + '@shikijs/engine-oniguruma@3.23.0': + resolution: {integrity: sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==} + + '@shikijs/langs@3.23.0': + resolution: {integrity: sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==} + + '@shikijs/rehype@3.23.0': + resolution: {integrity: sha512-GepKJxXHbXFfAkiZZZ+4V7x71Lw3s0ALYmydUxJRdvpKjSx9FOMSaunv6WRLFBXR6qjYerUq1YZQno+2gLEPwA==} + + '@shikijs/themes@3.23.0': + resolution: {integrity: sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==} + + '@shikijs/transformers@3.23.0': + resolution: {integrity: sha512-F9msZVxdF+krQNSdQ4V+Ja5QemeAoTQ2jxt7nJCwhDsdF1JWS3KxIQXA3lQbyKwS3J61oHRUSv4jYWv3CkaKTQ==} + + '@shikijs/types@3.23.0': + resolution: {integrity: sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@supabase/auth-js@2.99.1': + resolution: {integrity: sha512-x7lKKTvKjABJt/FYcRSPiTT01Xhm2FF8RhfL8+RHMkmlwmRQ88/lREupIHKwFPW0W6pTCJqkZb7Yhpw/EZ+fNw==} + engines: {node: '>=20.0.0'} + + '@supabase/functions-js@2.99.1': + resolution: {integrity: sha512-WQE62W5geYImCO4jzFxCk/avnK7JmOdtqu2eiPz3zOaNiIJajNRSAwMMDgEGd2EMs+sUVYj1LfBjfmW3EzHgIA==} + engines: {node: '>=20.0.0'} + + '@supabase/postgrest-js@2.99.1': + resolution: {integrity: sha512-gtw2ibJrADvfqrpUWXGNlrYUvxttF4WVWfPpTFKOb2IRj7B6YRWMDgcrYqIuD4ZEabK4m6YKQCCGy6clgf1lPA==} + engines: {node: '>=20.0.0'} + + '@supabase/realtime-js@2.99.1': + resolution: {integrity: sha512-9EDdy/5wOseGFqxW88ShV9JMRhm7f+9JGY5x+LqT8c7R0X1CTLwg5qie8FiBWcXTZ+68yYxVWunI+7W4FhkWOg==} + engines: {node: '>=20.0.0'} + + '@supabase/storage-js@2.99.1': + resolution: {integrity: sha512-mf7zPfqofI62SOoyQJeNUVxe72E4rQsbWim6lTDPeLu3lHija/cP5utlQADGrjeTgOUN6znx/rWn7SjrETP1dw==} + engines: {node: '>=20.0.0'} + + '@supabase/supabase-js@2.99.1': + resolution: {integrity: sha512-5MRoYD9ffXq8F6a036dm65YoSHisC3by/d22mauKE99Vrwf792KxYIIr/iqCX7E4hkuugbPZ5EGYHTB7MKy6Vg==} + engines: {node: '>=20.0.0'} + + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + + '@tailwindcss/node@4.2.1': + resolution: {integrity: sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==} + + '@tailwindcss/oxide-android-arm64@4.2.1': + resolution: {integrity: sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.2.1': + resolution: {integrity: sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.2.1': + resolution: {integrity: sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.2.1': + resolution: {integrity: sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': + resolution: {integrity: sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': + resolution: {integrity: sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.2.1': + resolution: {integrity: sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.2.1': + resolution: {integrity: sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.2.1': + resolution: {integrity: sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.2.1': + resolution: {integrity: sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': + resolution: {integrity: sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.2.1': + resolution: {integrity: sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.2.1': + resolution: {integrity: sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==} + engines: {node: '>= 20'} + + '@tailwindcss/postcss@4.2.1': + resolution: {integrity: sha512-OEwGIBnXnj7zJeonOh6ZG9woofIjGrd2BORfvE5p9USYKDCZoQmfqLcfNiRWoJlRWLdNPn2IgVZuWAOM4iTYMw==} + + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/mdx@2.0.13': + resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node@22.19.15': + resolution: {integrity: sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==} + + '@types/phoenix@1.6.7': + resolution: {integrity: sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.14': + resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + + astring@1.9.0: + resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} + hasBin: true + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + caniuse-lite@1.0.30001778: + resolution: {integrity: sha512-PN7uxFL+ExFJO61aVmP1aIEG4i9whQd4eoSCebav62UwDyp5OHh06zN4jqKSMePVgxHifCw1QJxdRkA1Pisekg==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + collapse-white-space@2.1.0: + resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + compute-scroll-into-view@3.1.1: + resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + enhanced-resolve@5.20.0: + resolution: {integrity: sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==} + engines: {node: '>=10.13.0'} + + esast-util-from-estree@2.0.0: + resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} + + esast-util-from-js@2.0.1: + resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} + + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + estree-util-attach-comments@3.0.0: + resolution: {integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==} + + estree-util-build-jsx@3.0.1: + resolution: {integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==} + + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + estree-util-scope@1.0.0: + resolution: {integrity: sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==} + + estree-util-to-js@2.0.0: + resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==} + + estree-util-value-to-estree@3.5.0: + resolution: {integrity: sha512-aMV56R27Gv3QmfmF1MY12GWkGzzeAezAX+UplqHVASfjc9wNzI/X6hC0S9oxq61WT4aQesLGslWP9tKk6ghRZQ==} + + estree-util-visit@2.0.0: + resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fumadocs-core@15.8.5: + resolution: {integrity: sha512-hyJtKGuB2J/5y7tDfI1EnGMKlNbSXM5N5cpwvgCY0DcBJwFMDG/GpSpaVRzh3aWy67pAYDZFIwdtbKXBa/q5bg==} + peerDependencies: + '@mixedbread/sdk': ^0.19.0 + '@oramacloud/client': 1.x.x || 2.x.x + '@tanstack/react-router': 1.x.x + '@types/react': '*' + algoliasearch: 5.x.x + lucide-react: '*' + next: 14.x.x || 15.x.x + react: 18.x.x || 19.x.x + react-dom: 18.x.x || 19.x.x + react-router: 7.x.x + waku: ^0.26.0 + peerDependenciesMeta: + '@mixedbread/sdk': + optional: true + '@oramacloud/client': + optional: true + '@tanstack/react-router': + optional: true + '@types/react': + optional: true + algoliasearch: + optional: true + lucide-react: + optional: true + next: + optional: true + react: + optional: true + react-dom: + optional: true + react-router: + optional: true + waku: + optional: true + + fumadocs-mdx@11.10.1: + resolution: {integrity: sha512-WoEzzzoKncXl7PM++GRxEplAb73y3A4ow+QdTYybhVtoYXgJzvTzkLc5OIlNQm72Dv+OxSAx7uk11zTTOX9YMQ==} + hasBin: true + peerDependencies: + '@fumadocs/mdx-remote': ^1.4.0 + fumadocs-core: ^14.0.0 || ^15.0.0 + next: ^15.3.0 + react: '*' + vite: 6.x.x || 7.x.x + peerDependenciesMeta: + '@fumadocs/mdx-remote': + optional: true + next: + optional: true + react: + optional: true + vite: + optional: true + + fumadocs-ui@15.8.5: + resolution: {integrity: sha512-9pyB+9rOOsrFnmmZ9xREp/OgVhyaSq2ocEpqTNbeQ7tlJ6JWbdFWfW0C9lRXprQEB6DJWUDtDxqKS5QXLH0EGA==} + peerDependencies: + '@types/react': '*' + next: 14.x.x || 15.x.x + react: 18.x.x || 19.x.x + react-dom: 18.x.x || 19.x.x + tailwindcss: ^3.4.14 || ^4.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + next: + optional: true + tailwindcss: + optional: true + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + github-slugger@2.0.0: + resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + hast-util-to-estree@3.1.3: + resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-to-string@3.0.1: + resolution: {integrity: sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + iceberg-js@0.8.1: + resolution: {integrity: sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==} + engines: {node: '>=20.0.0'} + + image-size@2.0.2: + resolution: {integrity: sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==} + engines: {node: '>=16.x'} + hasBin: true + + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + lightningcss-android-arm64@1.31.1: + resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.31.1: + resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.31.1: + resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.31.1: + resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.31.1: + resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.31.1: + resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.31.1: + resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.31.1: + resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.31.1: + resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.31.1: + resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.31.1: + resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.31.1: + resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==} + engines: {node: '>= 12.0.0'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + lru-cache@11.2.6: + resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} + engines: {node: 20 || >=22} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + markdown-extensions@2.0.0: + resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} + engines: {node: '>=16'} + + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdx@3.0.0: + resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-extension-mdx-expression@3.0.1: + resolution: {integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==} + + micromark-extension-mdx-jsx@3.0.2: + resolution: {integrity: sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==} + + micromark-extension-mdx-md@2.0.0: + resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==} + + micromark-extension-mdxjs-esm@3.0.0: + resolution: {integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==} + + micromark-extension-mdxjs@3.0.0: + resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-mdx-expression@2.0.3: + resolution: {integrity: sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-events-to-acorn@2.0.3: + resolution: {integrity: sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + next-themes@0.4.6: + resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==} + peerDependencies: + react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + + next@15.5.12: + resolution: {integrity: sha512-Fi/wQ4Etlrn60rz78bebG1i1SR20QxvV8tVp6iJspjLUSHcZoeUXCt+vmWoEcza85ElZzExK/jJ/F6SvtGktjA==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + + npm-to-yarn@3.0.1: + resolution: {integrity: sha512-tt6PvKu4WyzPwWUzy/hvPFqn+uwXO0K1ZHka8az3NnrhWJDmSqI8ncWq0fkL0k/lmmi5tAC11FXwXuh0rFbt1A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + oniguruma-parser@0.12.1: + resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} + + oniguruma-to-es@4.3.4: + resolution: {integrity: sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==} + + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + + path-to-regexp@8.3.0: + resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + postcss-selector-parser@7.1.1: + resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} + engines: {node: '>=4'} + + postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.5.8: + resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} + engines: {node: ^10 || ^12 || >=14} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + + react-dom@19.2.4: + resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} + peerDependencies: + react: ^19.2.4 + + react-medium-image-zoom@5.4.1: + resolution: {integrity: sha512-DD2iZYaCfAwiQGR8AN62r/cDJYoXhezlYJc5HY4TzBUGuGge43CptG0f7m0PEIM72aN6GfpjohvY1yYdtCJB7g==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.2: + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react@19.2.4: + resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} + engines: {node: '>=0.10.0'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + recma-build-jsx@1.0.0: + resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} + + recma-jsx@1.0.1: + resolution: {integrity: sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + recma-parse@1.0.0: + resolution: {integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==} + + recma-stringify@1.0.0: + resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.1.0: + resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + + rehype-recma@1.0.0: + resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==} + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-mdx@3.1.1: + resolution: {integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + remark@15.0.1: + resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + scroll-into-view-if-needed@3.1.0: + resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} + + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + shiki@3.23.0: + resolution: {integrity: sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + + tailwind-merge@3.5.0: + resolution: {integrity: sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==} + + tailwindcss@4.2.1: + resolution: {integrity: sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==} + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position-from-estree@2.0.0: + resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + ws@8.19.0: + resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + zod@4.3.6: + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@emnapi/runtime@1.9.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + + '@floating-ui/core@1.7.5': + dependencies: + '@floating-ui/utils': 0.2.11 + + '@floating-ui/dom@1.7.6': + dependencies: + '@floating-ui/core': 1.7.5 + '@floating-ui/utils': 0.2.11 + + '@floating-ui/react-dom@2.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@floating-ui/dom': 1.7.6 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@floating-ui/utils@0.2.11': {} + + '@formatjs/intl-localematcher@0.6.2': + dependencies: + tslib: 2.8.1 + + '@img/colour@1.1.0': + optional: true + + '@img/sharp-darwin-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.4 + optional: true + + '@img/sharp-darwin-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 + optional: true + + '@img/sharp-linux-arm@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.4 + optional: true + + '@img/sharp-linux-ppc64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.4 + optional: true + + '@img/sharp-linux-riscv64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.2.4 + optional: true + + '@img/sharp-linux-s390x@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.4 + optional: true + + '@img/sharp-linux-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.34.5': + dependencies: + '@emnapi/runtime': 1.9.0 + optional: true + + '@img/sharp-win32-arm64@0.34.5': + optional: true + + '@img/sharp-win32-ia32@0.34.5': + optional: true + + '@img/sharp-win32-x64@0.34.5': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@mdx-js/mdx@3.1.1': + dependencies: + '@types/estree': 1.0.8 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdx': 2.0.13 + acorn: 8.16.0 + collapse-white-space: 2.1.0 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-util-scope: 1.0.0 + estree-walker: 3.0.3 + hast-util-to-jsx-runtime: 2.3.6 + markdown-extensions: 2.0.0 + recma-build-jsx: 1.0.0 + recma-jsx: 1.0.1(acorn@8.16.0) + recma-stringify: 1.0.0 + rehype-recma: 1.0.0 + remark-mdx: 3.1.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + source-map: 0.7.6 + unified: 11.0.5 + unist-util-position-from-estree: 2.0.0 + unist-util-stringify-position: 4.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@next/env@15.5.12': {} + + '@next/swc-darwin-arm64@15.5.12': + optional: true + + '@next/swc-darwin-x64@15.5.12': + optional: true + + '@next/swc-linux-arm64-gnu@15.5.12': + optional: true + + '@next/swc-linux-arm64-musl@15.5.12': + optional: true + + '@next/swc-linux-x64-gnu@15.5.12': + optional: true + + '@next/swc-linux-x64-musl@15.5.12': + optional: true + + '@next/swc-win32-arm64-msvc@15.5.12': + optional: true + + '@next/swc-win32-x64-msvc@15.5.12': + optional: true + + '@orama/orama@3.1.18': {} + + '@radix-ui/number@1.1.1': {} + + '@radix-ui/primitive@1.1.3': {} + + '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.14)(react@19.2.4)': + dependencies: + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-context@1.1.2(@types/react@19.2.14)(react@19.2.4)': + dependencies: + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.4) + aria-hidden: 1.2.6 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-direction@1.1.1(@types/react@19.2.14)(react@19.2.4)': + dependencies: + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.14)(react@19.2.4)': + dependencies: + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-id@1.1.1(@types/react@19.2.14)(react@19.2.4)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.4) + aria-hidden: 1.2.6 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@floating-ui/react-dom': 2.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/rect': 1.1.1 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-slot@1.2.3(@types/react@19.2.14)(react@19.2.4)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-slot@1.2.4(@types/react@19.2.14)(react@19.2.4)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.14)(react@19.2.4)': + dependencies: + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.14)(react@19.2.4)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.14)(react@19.2.4)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.14)(react@19.2.4)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.14)(react@19.2.4)': + dependencies: + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.14)(react@19.2.4)': + dependencies: + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.14)(react@19.2.4)': + dependencies: + '@radix-ui/rect': 1.1.1 + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-use-size@1.1.1(@types/react@19.2.14)(react@19.2.4)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/rect@1.1.1': {} + + '@shikijs/core@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.4 + + '@shikijs/engine-oniguruma@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + + '@shikijs/rehype@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + '@types/hast': 3.0.4 + hast-util-to-string: 3.0.1 + shiki: 3.23.0 + unified: 11.0.5 + unist-util-visit: 5.1.0 + + '@shikijs/themes@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + + '@shikijs/transformers@3.23.0': + dependencies: + '@shikijs/core': 3.23.0 + '@shikijs/types': 3.23.0 + + '@shikijs/types@3.23.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@standard-schema/spec@1.1.0': {} + + '@supabase/auth-js@2.99.1': + dependencies: + tslib: 2.8.1 + + '@supabase/functions-js@2.99.1': + dependencies: + tslib: 2.8.1 + + '@supabase/postgrest-js@2.99.1': + dependencies: + tslib: 2.8.1 + + '@supabase/realtime-js@2.99.1': + dependencies: + '@types/phoenix': 1.6.7 + '@types/ws': 8.18.1 + tslib: 2.8.1 + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@supabase/storage-js@2.99.1': + dependencies: + iceberg-js: 0.8.1 + tslib: 2.8.1 + + '@supabase/supabase-js@2.99.1': + dependencies: + '@supabase/auth-js': 2.99.1 + '@supabase/functions-js': 2.99.1 + '@supabase/postgrest-js': 2.99.1 + '@supabase/realtime-js': 2.99.1 + '@supabase/storage-js': 2.99.1 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 + + '@tailwindcss/node@4.2.1': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.20.0 + jiti: 2.6.1 + lightningcss: 1.31.1 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.2.1 + + '@tailwindcss/oxide-android-arm64@4.2.1': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.2.1': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.2.1': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.2.1': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.2.1': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.2.1': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.2.1': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.2.1': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.2.1': + optional: true + + '@tailwindcss/oxide@4.2.1': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.2.1 + '@tailwindcss/oxide-darwin-arm64': 4.2.1 + '@tailwindcss/oxide-darwin-x64': 4.2.1 + '@tailwindcss/oxide-freebsd-x64': 4.2.1 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.1 + '@tailwindcss/oxide-linux-arm64-gnu': 4.2.1 + '@tailwindcss/oxide-linux-arm64-musl': 4.2.1 + '@tailwindcss/oxide-linux-x64-gnu': 4.2.1 + '@tailwindcss/oxide-linux-x64-musl': 4.2.1 + '@tailwindcss/oxide-wasm32-wasi': 4.2.1 + '@tailwindcss/oxide-win32-arm64-msvc': 4.2.1 + '@tailwindcss/oxide-win32-x64-msvc': 4.2.1 + + '@tailwindcss/postcss@4.2.1': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.2.1 + '@tailwindcss/oxide': 4.2.1 + postcss: 8.5.8 + tailwindcss: 4.2.1 + + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.8 + + '@types/estree@1.0.8': {} + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdx@2.0.13': {} + + '@types/ms@2.1.0': {} + + '@types/node@22.19.15': + dependencies: + undici-types: 6.21.0 + + '@types/phoenix@1.6.7': {} + + '@types/react-dom@19.2.3(@types/react@19.2.14)': + dependencies: + '@types/react': 19.2.14 + + '@types/react@19.2.14': + dependencies: + csstype: 3.2.3 + + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@types/ws@8.18.1': + dependencies: + '@types/node': 22.19.15 + + '@ungap/structured-clone@1.3.0': {} + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + argparse@2.0.1: {} + + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + + astring@1.9.0: {} + + bail@2.0.2: {} + + caniuse-lite@1.0.30001778: {} + + ccount@2.0.1: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + client-only@0.0.1: {} + + clsx@2.1.1: {} + + collapse-white-space@2.1.0: {} + + comma-separated-tokens@2.0.3: {} + + compute-scroll-into-view@3.1.1: {} + + cssesc@3.0.0: {} + + csstype@3.2.3: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + + dequal@2.0.3: {} + + detect-libc@2.1.2: {} + + detect-node-es@1.1.0: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + enhanced-resolve@5.20.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + + esast-util-from-estree@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + unist-util-position-from-estree: 2.0.0 + + esast-util-from-js@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + acorn: 8.16.0 + esast-util-from-estree: 2.0.0 + vfile-message: 4.0.3 + + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + + escape-string-regexp@5.0.0: {} + + estree-util-attach-comments@3.0.0: + dependencies: + '@types/estree': 1.0.8 + + estree-util-build-jsx@3.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-walker: 3.0.3 + + estree-util-is-identifier-name@3.0.0: {} + + estree-util-scope@1.0.0: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + + estree-util-to-js@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + astring: 1.9.0 + source-map: 0.7.6 + + estree-util-value-to-estree@3.5.0: + dependencies: + '@types/estree': 1.0.8 + + estree-util-visit@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/unist': 3.0.3 + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + extend@3.0.2: {} + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fumadocs-core@15.8.5(@types/react@19.2.14)(next@15.5.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + '@formatjs/intl-localematcher': 0.6.2 + '@orama/orama': 3.1.18 + '@shikijs/rehype': 3.23.0 + '@shikijs/transformers': 3.23.0 + github-slugger: 2.0.0 + hast-util-to-estree: 3.1.3 + hast-util-to-jsx-runtime: 2.3.6 + image-size: 2.0.2 + negotiator: 1.0.0 + npm-to-yarn: 3.0.1 + path-to-regexp: 8.3.0 + react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.4) + remark: 15.0.1 + remark-gfm: 4.0.1 + remark-rehype: 11.1.2 + scroll-into-view-if-needed: 3.1.0 + shiki: 3.23.0 + unist-util-visit: 5.1.0 + optionalDependencies: + '@types/react': 19.2.14 + next: 15.5.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - supports-color + + fumadocs-mdx@11.10.1(fumadocs-core@15.8.5(@types/react@19.2.14)(next@15.5.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(next@15.5.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4): + dependencies: + '@mdx-js/mdx': 3.1.1 + '@standard-schema/spec': 1.1.0 + chokidar: 4.0.3 + esbuild: 0.25.12 + estree-util-value-to-estree: 3.5.0 + fumadocs-core: 15.8.5(@types/react@19.2.14)(next@15.5.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + js-yaml: 4.1.1 + lru-cache: 11.2.6 + picocolors: 1.1.1 + remark-mdx: 3.1.1 + remark-parse: 11.0.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + unified: 11.0.5 + unist-util-visit: 5.1.0 + zod: 4.3.6 + optionalDependencies: + next: 15.5.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: 19.2.4 + transitivePeerDependencies: + - supports-color + + fumadocs-ui@15.8.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(next@15.5.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tailwindcss@4.2.1): + dependencies: + '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-navigation-menu': 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-scroll-area': 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-slot': 1.2.4(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + class-variance-authority: 0.7.1 + fumadocs-core: 15.8.5(@types/react@19.2.14)(next@15.5.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + lodash.merge: 4.6.2 + next-themes: 0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + postcss-selector-parser: 7.1.1 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + react-medium-image-zoom: 5.4.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + scroll-into-view-if-needed: 3.1.0 + tailwind-merge: 3.5.0 + optionalDependencies: + '@types/react': 19.2.14 + next: 15.5.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + tailwindcss: 4.2.1 + transitivePeerDependencies: + - '@mixedbread/sdk' + - '@oramacloud/client' + - '@tanstack/react-router' + - '@types/react-dom' + - algoliasearch + - lucide-react + - react-router + - supports-color + - waku + + get-nonce@1.0.1: {} + + github-slugger@2.0.0: {} + + graceful-fs@4.2.11: {} + + hast-util-to-estree@3.1.3: + dependencies: + '@types/estree': 1.0.8 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-attach-comments: 3.0.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + zwitch: 2.0.4 + transitivePeerDependencies: + - supports-color + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-to-string@3.0.1: + dependencies: + '@types/hast': 3.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + html-void-elements@3.0.0: {} + + iceberg-js@0.8.1: {} + + image-size@2.0.2: {} + + inline-style-parser@0.2.7: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-decimal@2.0.1: {} + + is-hexadecimal@2.0.1: {} + + is-plain-obj@4.1.0: {} + + jiti@2.6.1: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + lightningcss-android-arm64@1.31.1: + optional: true + + lightningcss-darwin-arm64@1.31.1: + optional: true + + lightningcss-darwin-x64@1.31.1: + optional: true + + lightningcss-freebsd-x64@1.31.1: + optional: true + + lightningcss-linux-arm-gnueabihf@1.31.1: + optional: true + + lightningcss-linux-arm64-gnu@1.31.1: + optional: true + + lightningcss-linux-arm64-musl@1.31.1: + optional: true + + lightningcss-linux-x64-gnu@1.31.1: + optional: true + + lightningcss-linux-x64-musl@1.31.1: + optional: true + + lightningcss-win32-arm64-msvc@1.31.1: + optional: true + + lightningcss-win32-x64-msvc@1.31.1: + optional: true + + lightningcss@1.31.1: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.31.1 + lightningcss-darwin-arm64: 1.31.1 + lightningcss-darwin-x64: 1.31.1 + lightningcss-freebsd-x64: 1.31.1 + lightningcss-linux-arm-gnueabihf: 1.31.1 + lightningcss-linux-arm64-gnu: 1.31.1 + lightningcss-linux-arm64-musl: 1.31.1 + lightningcss-linux-x64-gnu: 1.31.1 + lightningcss-linux-x64-musl: 1.31.1 + lightningcss-win32-arm64-msvc: 1.31.1 + lightningcss-win32-x64-msvc: 1.31.1 + + lodash.merge@4.6.2: {} + + longest-streak@3.1.0: {} + + lru-cache@11.2.6: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + markdown-extensions@2.0.0: {} + + markdown-table@3.0.4: {} + + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + mdast-util-from-markdown@2.0.3: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.3 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx@3.0.0: + dependencies: + mdast-util-from-markdown: 2.0.3 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-mdx-expression@3.0.1: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + micromark-factory-mdx-expression: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-mdx-jsx@3.0.2: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + micromark-factory-mdx-expression: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + vfile-message: 4.0.3 + + micromark-extension-mdx-md@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-mdxjs-esm@3.0.0: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.3 + + micromark-extension-mdxjs@3.0.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + micromark-extension-mdx-expression: 3.0.1 + micromark-extension-mdx-jsx: 3.0.2 + micromark-extension-mdx-md: 2.0.0 + micromark-extension-mdxjs-esm: 3.0.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-mdx-expression@2.0.3: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.3 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.3.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-events-to-acorn@2.0.3: + dependencies: + '@types/estree': 1.0.8 + '@types/unist': 3.0.3 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + vfile-message: 4.0.3 + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.12 + debug: 4.4.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + negotiator@1.0.0: {} + + next-themes@0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + next@15.5.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + '@next/env': 15.5.12 + '@swc/helpers': 0.5.15 + caniuse-lite: 1.0.30001778 + postcss: 8.4.31 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + styled-jsx: 5.1.6(react@19.2.4) + optionalDependencies: + '@next/swc-darwin-arm64': 15.5.12 + '@next/swc-darwin-x64': 15.5.12 + '@next/swc-linux-arm64-gnu': 15.5.12 + '@next/swc-linux-arm64-musl': 15.5.12 + '@next/swc-linux-x64-gnu': 15.5.12 + '@next/swc-linux-x64-musl': 15.5.12 + '@next/swc-win32-arm64-msvc': 15.5.12 + '@next/swc-win32-x64-msvc': 15.5.12 + sharp: 0.34.5 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + + npm-to-yarn@3.0.1: {} + + oniguruma-parser@0.12.1: {} + + oniguruma-to-es@4.3.4: + dependencies: + oniguruma-parser: 0.12.1 + regex: 6.1.0 + regex-recursion: 6.0.2 + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.3.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + path-to-regexp@8.3.0: {} + + picocolors@1.1.1: {} + + picomatch@4.0.3: {} + + postcss-selector-parser@7.1.1: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss@8.4.31: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postcss@8.5.8: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + property-information@7.1.0: {} + + react-dom@19.2.4(react@19.2.4): + dependencies: + react: 19.2.4 + scheduler: 0.27.0 + + react-medium-image-zoom@5.4.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + react-remove-scroll-bar@2.3.8(@types/react@19.2.14)(react@19.2.4): + dependencies: + react: 19.2.4 + react-style-singleton: 2.2.3(@types/react@19.2.14)(react@19.2.4) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.14 + + react-remove-scroll@2.7.2(@types/react@19.2.14)(react@19.2.4): + dependencies: + react: 19.2.4 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.14)(react@19.2.4) + react-style-singleton: 2.2.3(@types/react@19.2.14)(react@19.2.4) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.2.14)(react@19.2.4) + use-sidecar: 1.1.3(@types/react@19.2.14)(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + + react-style-singleton@2.2.3(@types/react@19.2.14)(react@19.2.4): + dependencies: + get-nonce: 1.0.1 + react: 19.2.4 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.14 + + react@19.2.4: {} + + readdirp@4.1.2: {} + + recma-build-jsx@1.0.0: + dependencies: + '@types/estree': 1.0.8 + estree-util-build-jsx: 3.0.1 + vfile: 6.0.3 + + recma-jsx@1.0.1(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + estree-util-to-js: 2.0.0 + recma-parse: 1.0.0 + recma-stringify: 1.0.0 + unified: 11.0.5 + + recma-parse@1.0.0: + dependencies: + '@types/estree': 1.0.8 + esast-util-from-js: 2.0.1 + unified: 11.0.5 + vfile: 6.0.3 + + recma-stringify@1.0.0: + dependencies: + '@types/estree': 1.0.8 + estree-util-to-js: 2.0.0 + unified: 11.0.5 + vfile: 6.0.3 + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.1.0: + dependencies: + regex-utilities: 2.3.0 + + rehype-recma@1.0.0: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + hast-util-to-estree: 3.1.3 + transitivePeerDependencies: + - supports-color + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-mdx@3.1.1: + dependencies: + mdast-util-mdx: 3.0.0 + micromark-extension-mdxjs: 3.0.0 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + remark@15.0.1: + dependencies: + '@types/mdast': 4.0.4 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + scheduler@0.27.0: {} + + scroll-into-view-if-needed@3.1.0: + dependencies: + compute-scroll-into-view: 3.1.1 + + semver@7.7.4: + optional: true + + sharp@0.34.5: + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.7.4 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 + optional: true + + shiki@3.23.0: + dependencies: + '@shikijs/core': 3.23.0 + '@shikijs/engine-javascript': 3.23.0 + '@shikijs/engine-oniguruma': 3.23.0 + '@shikijs/langs': 3.23.0 + '@shikijs/themes': 3.23.0 + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + source-map-js@1.2.1: {} + + source-map@0.7.6: {} + + space-separated-tokens@2.0.2: {} + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + style-to-js@1.1.21: + dependencies: + style-to-object: 1.0.14 + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + + styled-jsx@5.1.6(react@19.2.4): + dependencies: + client-only: 0.0.1 + react: 19.2.4 + + tailwind-merge@3.5.0: {} + + tailwindcss@4.2.1: {} + + tapable@2.3.0: {} + + tinyexec@1.0.2: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + trim-lines@3.0.1: {} + + trough@2.2.0: {} + + tslib@2.8.1: {} + + typescript@5.9.3: {} + + undici-types@6.21.0: {} + + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position-from-estree@2.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + use-callback-ref@1.3.3(@types/react@19.2.14)(react@19.2.4): + dependencies: + react: 19.2.4 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.14 + + use-sidecar@1.1.3(@types/react@19.2.14)(react@19.2.4): + dependencies: + detect-node-es: 1.1.0 + react: 19.2.4 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.14 + + util-deprecate@1.0.2: {} + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + ws@8.19.0: {} + + zod@4.3.6: {} + + zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..cf4cfeb --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +packages: + - "website" + - "marketplace" + - "packages/*" diff --git a/profiles/data-engineer.yaml b/profiles/data-engineer.yaml new file mode 100644 index 0000000..5c59ca0 --- /dev/null +++ b/profiles/data-engineer.yaml @@ -0,0 +1,44 @@ +# Data Engineer Profile +# Pre-configured harness-kit setup for data engineering workflows +# +# Install: /plugin marketplace add harnessprotocol/harness-kit +# then install this profile's components + +name: data-engineer +description: > + Harness configuration for data engineers working with SQL pipelines, + data lineage, and knowledge management. Includes lineage tracing, + research tooling for documentation, and session capture for + compounding institutional knowledge. + +author: + name: harnessprotocol + +components: + - name: data-lineage + version: "0.2.0" + - name: research + version: "0.2.0" + - name: orient + version: "0.2.0" + - name: capture-session + version: "0.2.0" + - name: explain + version: "0.2.0" + - name: docgen + version: "0.2.0" + - name: review + version: "0.2.0" + +knowledge: + backend: memory-md + seed_docs: + - topic: sql-patterns + description: Common SQL patterns and anti-patterns for data pipelines + - topic: data-quality + description: Data quality checks and validation strategies + +rules: + - Always trace lineage impact before modifying shared tables + - Prefer CTEs over nested subqueries for readability + - Document column-level transformations in commit messages diff --git a/profiles/full-stack-engineer.yaml b/profiles/full-stack-engineer.yaml new file mode 100644 index 0000000..4dfe044 --- /dev/null +++ b/profiles/full-stack-engineer.yaml @@ -0,0 +1,41 @@ +# Full-Stack Engineer Profile +# Pre-configured harness-kit setup for full-stack development workflows +# +# Install: /plugin marketplace add harnessprotocol/harness-kit +# then install this profile's components + +name: full-stack-engineer +description: > + Harness configuration for full-stack engineers shipping features + end-to-end. Includes code review, PR workflows, documentation + generation, and code explanation tools. + +author: + name: harnessprotocol + +components: + - name: review + version: "0.2.0" + - name: ship-pr + version: "0.1.0" + - name: pull-request-sweep + version: "0.1.0" + - name: explain + version: "0.2.0" + - name: docgen + version: "0.2.0" + - name: harness-share + version: "0.3.0" + +knowledge: + backend: memory-md + seed_docs: + - topic: architecture + description: Project architecture decisions and conventions + - topic: testing + description: Testing strategies and patterns + +rules: + - Run tests before opening PRs + - Write descriptive commit messages with conventional commit prefixes + - Review your own diff before requesting review from others diff --git a/profiles/research-knowledge.yaml b/profiles/research-knowledge.yaml new file mode 100644 index 0000000..e2caa98 --- /dev/null +++ b/profiles/research-knowledge.yaml @@ -0,0 +1,39 @@ +# Research & Knowledge Profile +# Pre-configured harness-kit setup for research-heavy workflows +# +# Install: /plugin marketplace add harnessprotocol/harness-kit +# then install this profile's components + +name: research-knowledge +description: > + Harness configuration for roles focused on research, learning, and + knowledge management. Ideal for technical writers, analysts, and + anyone building a compounding knowledge base from diverse sources. + +author: + name: harnessprotocol + +components: + - name: research + version: "0.2.0" + - name: orient + version: "0.2.0" + - name: capture-session + version: "0.2.0" + - name: explain + version: "0.2.0" + - name: docgen + version: "0.2.0" + +knowledge: + backend: memory-md + seed_docs: + - topic: research-methodology + description: Research workflows and source evaluation criteria + - topic: knowledge-taxonomy + description: How topics and concepts are organized in the knowledge graph + +rules: + - Always cite sources when adding to the knowledge base + - Capture session insights before ending long research sessions + - Orient at the start of each session to build on prior work diff --git a/website/content/docs/plugins/explain.md b/website/content/docs/plugins/code-quality/explain.mdx similarity index 89% rename from website/content/docs/plugins/explain.md rename to website/content/docs/plugins/code-quality/explain.mdx index 725655f..b87c408 100644 --- a/website/content/docs/plugins/explain.md +++ b/website/content/docs/plugins/code-quality/explain.mdx @@ -3,6 +3,8 @@ sidebar_position: 3 title: explain --- +> **[View in Marketplace →](https://marketplace.harnessprotocol.io/plugins/explain)** for install tracking, related plugins, and profiles. + # explain Layered explanations of files, functions, directories, or concepts. @@ -86,3 +88,11 @@ If a project has a CLAUDE.md with an Architecture section, the explanation uses ### Why conversation output? Most of the time you want to understand something quickly, not create documentation. If you want to save an explanation, just ask. + +## Runtime Configuration + +{/* @embed plugins/explain/skills/explain/SKILL.md */} + +## Plugin Documentation + +{/* @embed plugins/explain/skills/explain/README.md */} diff --git a/website/content/docs/plugins/code-quality/meta.json b/website/content/docs/plugins/code-quality/meta.json new file mode 100644 index 0000000..8b0318f --- /dev/null +++ b/website/content/docs/plugins/code-quality/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Code Quality", + "pages": ["review", "explain"] +} diff --git a/website/content/docs/plugins/review.md b/website/content/docs/plugins/code-quality/review.mdx similarity index 90% rename from website/content/docs/plugins/review.md rename to website/content/docs/plugins/code-quality/review.mdx index 3f11aa8..dc0aae5 100644 --- a/website/content/docs/plugins/review.md +++ b/website/content/docs/plugins/code-quality/review.mdx @@ -3,6 +3,8 @@ sidebar_position: 7 title: review --- +> **[View in Marketplace →](https://marketplace.harnessprotocol.io/plugins/review)** for install tracking, related plugins, and profiles. + # review Structured code review for local branches, open PRs, or scoped paths. @@ -97,3 +99,11 @@ Not all concerns are equal. Distinguishing BLOCKERs from NITs prevents review fa ### Why cross-file analysis? Single-file reviews miss systemic issues. The cross-file pass catches the problems that live between files — missing tests, broken contracts, and inconsistent renames. + +## Runtime Configuration + +{/* @embed plugins/review/skills/review/SKILL.md */} + +## Plugin Documentation + +{/* @embed plugins/review/skills/review/README.md */} diff --git a/website/content/docs/plugins/data-lineage.md b/website/content/docs/plugins/data-engineering/data-lineage.mdx similarity index 91% rename from website/content/docs/plugins/data-lineage.md rename to website/content/docs/plugins/data-engineering/data-lineage.mdx index d0ed925..f46ce04 100644 --- a/website/content/docs/plugins/data-lineage.md +++ b/website/content/docs/plugins/data-engineering/data-lineage.mdx @@ -3,6 +3,8 @@ sidebar_position: 4 title: data-lineage --- +> **[View in Marketplace →](https://marketplace.harnessprotocol.io/plugins/data-lineage)** for install tracking, related plugins, and profiles. + # data-lineage Column-level lineage tracing through SQL, Kafka, Spark, and JDBC codebases. @@ -100,3 +102,11 @@ In messy stacks, not every hop can be traced with certainty. Rather than pretend ### Why SVG? Self-contained, opens in any browser, requires no dependencies. Can be shared, embedded in documentation, or viewed alongside code. ASCII diagrams are available as a fallback. + +## Runtime Configuration + +{/* @embed plugins/data-lineage/skills/data-lineage/SKILL.md */} + +## Plugin Documentation + +{/* @embed plugins/data-lineage/skills/data-lineage/README.md */} diff --git a/website/content/docs/plugins/data-engineering/meta.json b/website/content/docs/plugins/data-engineering/meta.json new file mode 100644 index 0000000..1e560f6 --- /dev/null +++ b/website/content/docs/plugins/data-engineering/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Data Engineering", + "pages": ["data-lineage"] +} diff --git a/website/content/docs/plugins/devops/meta.json b/website/content/docs/plugins/devops/meta.json new file mode 100644 index 0000000..e3e7485 --- /dev/null +++ b/website/content/docs/plugins/devops/meta.json @@ -0,0 +1,4 @@ +{ + "title": "DevOps", + "pages": ["ship-pr", "pull-request-sweep"] +} diff --git a/website/content/docs/plugins/devops/pull-request-sweep.mdx b/website/content/docs/plugins/devops/pull-request-sweep.mdx new file mode 100644 index 0000000..f5a5e72 --- /dev/null +++ b/website/content/docs/plugins/devops/pull-request-sweep.mdx @@ -0,0 +1,62 @@ +--- +title: pull-request-sweep +--- + +> **[View in Marketplace →](https://marketplace.harnessprotocol.io/plugins/pull-request-sweep)** for install tracking, related plugins, and profiles. + +# pull-request-sweep + +Cross-repo PR sweep — discover, triage, review, merge, and report on all your open PRs. + +## Requirements + +- **Claude Code** — required +- **`gh` CLI** ([GitHub CLI](https://cli.github.com/)) — required for PR discovery, CI checks, merging, and rebasing. `GH_TOKEN` declared as required in `plugin.json` — see [Secrets Management](/docs/guides/secrets-management) for setup. +- **review plugin** — used for automated code reviews on unreviewed PRs + +## Install + +``` +/plugin marketplace add harnessprotocol/harness-kit +/plugin install pull-request-sweep@harness-kit +``` + +## What It Does + +When you invoke `/pull-request-sweep`: + +1. Discovers all open (non-draft) PRs authored by you across all repos +2. Enriches each PR with CI status and merge state +3. Triages into categories: MERGE_READY, PENDING_CI, NEEDS_REVIEW, BEHIND_BASE, CI_FAILING, CHANGES_REQUESTED, CONFLICTING, BLOCKED +4. Shows triage table and waits for your confirmation +5. Acts: merges ready PRs, rebases behind ones, fixes quick CI failures (lint/format/typo), runs code reviews on unreviewed PRs +6. Produces a full report + +## Usage + +``` +/pull-request-sweep +``` + +Or use natural language: + +``` +Check my PRs. +What's the status of all my open PRs? +Sweep my PRs. +``` + +## Notes + +- Never acts on draft PRs +- Never auto-resolves CHANGES_REQUESTED — flags for human follow-up +- Safe to run repeatedly — idempotent, already-merged PRs won't reappear +- Pairs with `/loop 30m /pull-request-sweep` for ongoing PR babysitting + +## Runtime Configuration + +{/* @embed plugins/pull-request-sweep/skills/pull-request-sweep/SKILL.md */} + +## Plugin Documentation + +{/* @embed plugins/pull-request-sweep/skills/pull-request-sweep/README.md */} diff --git a/website/content/docs/plugins/devops/ship-pr.mdx b/website/content/docs/plugins/devops/ship-pr.mdx new file mode 100644 index 0000000..701229d --- /dev/null +++ b/website/content/docs/plugins/devops/ship-pr.mdx @@ -0,0 +1,60 @@ +--- +title: ship-pr +--- + +> **[View in Marketplace →](https://marketplace.harnessprotocol.io/plugins/ship-pr)** for install tracking, related plugins, and profiles. + +# ship-pr + +End-of-task shipping workflow — run tests, open a PR, code review, fix CI, and squash merge. + +## Requirements + +- **Claude Code** — required +- **`gh` CLI** ([GitHub CLI](https://cli.github.com/)) — required for PR creation, CI status checks, and merging. `GH_TOKEN` declared as required in `plugin.json` — see [Secrets Management](/docs/guides/secrets-management) for setup. +- **review plugin** — used for automated code review before merge + +## Install + +``` +/plugin marketplace add harnessprotocol/harness-kit +/plugin install ship-pr@harness-kit +``` + +## What It Does + +When you invoke `/ship-pr`: + +1. Runs local tests +2. Creates a PR with a structured description (summary, test plan) +3. Runs a code review via subagent — flags MUST FIX issues before proceeding +4. Checks CI — attempts quick fixes (lint, format, typos) if failing +5. Verifies the branch is up-to-date with base +6. Confirms with you, then squash merges and cleans up the branch + +## Usage + +``` +/ship-pr +``` + +Or use natural language: + +``` +I'm done with this feature, let's ship it. +Wrap this up and open a PR. +``` + +## Notes + +- Skips drafts — draft PRs are not shipped +- "Just ship it" mode available if you want to skip the confirmation gate +- Uses the `review` skill for code review — install it alongside this plugin + +## Runtime Configuration + +{/* @embed plugins/ship-pr/skills/ship-pr/SKILL.md */} + +## Plugin Documentation + +{/* @embed plugins/ship-pr/skills/ship-pr/README.md */} diff --git a/website/content/docs/plugins/docgen.md b/website/content/docs/plugins/documentation/docgen.mdx similarity index 90% rename from website/content/docs/plugins/docgen.md rename to website/content/docs/plugins/documentation/docgen.mdx index 0d1f820..41047df 100644 --- a/website/content/docs/plugins/docgen.md +++ b/website/content/docs/plugins/documentation/docgen.mdx @@ -3,6 +3,8 @@ sidebar_position: 8 title: docgen --- +> **[View in Marketplace →](https://marketplace.harnessprotocol.io/plugins/docgen)** for install tracking, related plugins, and profiles. + # docgen Generate or update project documentation from the codebase — READMEs, API references, architecture overviews, and changelogs. @@ -83,3 +85,11 @@ The best docs combine generated structure with hand-written explanations. Blowin ### Why style matching? A README that uses `##` top-level headings in a project that uses `#` looks wrong. Matching existing style makes generated docs feel native, not bolted on. + +## Runtime Configuration + +{/* @embed plugins/docgen/skills/docgen/SKILL.md */} + +## Plugin Documentation + +{/* @embed plugins/docgen/skills/docgen/README.md */} diff --git a/website/content/docs/plugins/documentation/meta.json b/website/content/docs/plugins/documentation/meta.json new file mode 100644 index 0000000..ebf8c15 --- /dev/null +++ b/website/content/docs/plugins/documentation/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Documentation", + "pages": ["docgen"] +} diff --git a/website/content/docs/plugins/meta.json b/website/content/docs/plugins/meta.json index abd1e6c..e2ec8d1 100644 --- a/website/content/docs/plugins/meta.json +++ b/website/content/docs/plugins/meta.json @@ -1,13 +1,4 @@ { "title": "Plugins", - "pages": [ - "overview", - "research", - "explain", - "data-lineage", - "orient", - "capture-session", - "review", - "docgen" - ] + "pages": ["overview", "research-knowledge", "code-quality", "data-engineering", "documentation", "devops", "productivity"] } diff --git a/website/content/docs/plugins/overview.md b/website/content/docs/plugins/overview.md index 4cfe2d6..0a9da8e 100644 --- a/website/content/docs/plugins/overview.md +++ b/website/content/docs/plugins/overview.md @@ -5,25 +5,28 @@ title: Plugin Overview # Plugins -harness-kit ships 7 plugins, all at v0.2.0. Each packages a proven workflow as a portable prompt template, currently distributed through Claude Code's plugin marketplace. +harness-kit ships 10 plugins across 6 categories. Each packages a proven workflow as a portable prompt template, currently distributed through Claude Code's plugin marketplace. ## At a Glance | Plugin | What it does | Dependencies | |--------|-------------|-------------| -| [research](research) | Process any source into a structured, compounding knowledge base | `gh` CLI (GitHub only), Python 3.10+ | -| [explain](explain) | Layered explanations of files, functions, directories, or concepts | None | -| [data-lineage](data-lineage) | Column-level lineage tracing through SQL, Kafka, Spark, JDBC | None | -| [orient](orient) | Topic-focused session orientation across graph, knowledge, and research | None (optional: MCP Memory Server) | -| [capture-session](capture-session) | Capture session information into a staging file for later reflection | None | -| [review](review) | Structured code review for branches, PRs, or paths with severity labels | `gh` CLI (PR review only) | -| [docgen](docgen) | Generate or update README, API docs, architecture overview, or changelog | None | +| [research](research-knowledge/research) | Process any source into a structured, compounding knowledge base | `gh` CLI (GitHub only), Python 3.10+ | +| [orient](research-knowledge/orient) | Topic-focused session orientation across graph, knowledge, and research | None (optional: MCP Memory Server) | +| [capture-session](research-knowledge/capture-session) | Capture session information into a staging file for later reflection | None | +| [review](code-quality/review) | Structured code review for branches, PRs, or paths with severity labels | `gh` CLI (PR review only) | +| [explain](code-quality/explain) | Layered explanations of files, functions, directories, or concepts | None | +| [data-lineage](data-engineering/data-lineage) | Column-level lineage tracing through SQL, Kafka, Spark, JDBC | None | +| [docgen](documentation/docgen) | Generate or update README, API docs, architecture overview, or changelog | None | +| [ship-pr](devops/ship-pr) | End-of-task workflow: open a PR, code review, fix CI, squash merge | `gh` CLI | +| [pull-request-sweep](devops/pull-request-sweep) | Cross-repo PR sweep: triage, review, merge, fix CI | `gh` CLI | +| [harness-share](productivity/harness-share) | Compile, export, import, and sync harness configs across AI tools | None | -Plugin dependencies are formally declared in `plugin.json` under `requires`. See [Secrets & Configuration](/docs/concepts/secrets-management) for the full schema. +Plugin dependencies are formally declared in `plugin.json` under `requires`. See [Secrets & Configuration](/docs/concepts/secrets-management) for the schema and [Secrets Management](/docs/guides/secrets-management) for setup instructions. ## Install any plugin -``` +```bash /plugin marketplace add harnessprotocol/harness-kit /plugin install @harness-kit ``` @@ -48,3 +51,15 @@ The **SKILL.md** is the runtime unit — it defines the workflow Claude Code exe SKILL.md files are plain markdown, not SDK code or API calls. While plugins currently distribute through Claude Code's marketplace, the prompt workflows are harness-agnostic — they work in any tool that reads prompt templates. See [Cross-Harness Portability](/docs/concepts/cross-harness-portability) for details. See [Plugins vs. Skills](/docs/concepts/plugins-vs-skills) for the full rationale. + +## Profiles + +Profiles are pre-configured collections of plugins for specific roles. Each profile is a `harness.yaml` that bundles a curated set of plugins with optional knowledge seeds. + +| Profile | Who it's for | Plugins included | +|---------|-------------|-----------------| +| [research-knowledge](/profiles/research-knowledge) | Research-focused roles | research, orient, capture-session, explain, docgen | +| [data-engineer](/profiles/data-engineer) | Data engineers with SQL pipelines | data-lineage, research, orient, capture-session, explain, docgen, review | +| [full-stack-engineer](/profiles/full-stack-engineer) | Full-stack feature shipping | review, ship-pr, pull-request-sweep, explain, docgen, harness-share | + +Install a profile's plugins individually, or use `/harness-import` with the profile's YAML to install them all at once. diff --git a/website/content/docs/plugins/productivity/harness-share.mdx b/website/content/docs/plugins/productivity/harness-share.mdx new file mode 100644 index 0000000..50e73f7 --- /dev/null +++ b/website/content/docs/plugins/productivity/harness-share.mdx @@ -0,0 +1,142 @@ +--- +title: harness-share +--- + +> **[View in Marketplace →](https://marketplace.harnessprotocol.io/plugins/harness-share)** for install tracking, related plugins, and profiles. + +# harness-share + +Compile, export, import, and sync harness configurations across Claude Code, Cursor, and GitHub Copilot. + +## Requirements + +None — works with any AI tool that supports skill files or instruction blocks. + +## Install + +``` +/plugin marketplace add harnessprotocol/harness-kit +/plugin install harness-share@harness-kit +``` + +## What It Does + +harness-share is a multi-skill plugin that manages your AI tool configuration as portable YAML. It includes five skills: + +| Skill | What it does | +|-------|-------------| +| `/harness-export` | Captures installed plugins into a shareable `harness.yaml` | +| `/harness-import` | Installs plugins from a shared `harness.yaml` config | +| `/harness-compile` | Compiles `harness.yaml` into native configs for Claude Code, Cursor, and Copilot | +| `/harness-sync` | Bidirectional sync of skills, MCP configs, and instructions across platforms | +| `/harness-validate` | Validates a `harness.yaml` before compiling or sharing | + +## Usage + +### Export your current setup + +``` +/harness-export +/harness-export ~/dotfiles/harness.yaml +``` + +Scans all AI tool directories, deduplicates installed plugins, and writes a `harness.yaml`. + +### Import a shared config + +``` +/harness-import +/harness-import path/to/harness.yaml +``` + +Presents plugins interactively — install all or pick a subset. Sets up Cursor and Copilot if detected. + +### Compile to native formats + +``` +/harness-compile +/harness-compile --target cursor +/harness-compile --dry-run +``` + +One `harness.yaml` produces native config files for every supported tool. Generated content is wrapped in section markers so re-compilation only updates harness-managed sections. + +### Sync across platforms + +``` +/harness-sync +/harness-sync --target cursor +``` + +Inventories skills, MCP configs, and instruction blocks on each platform, reports divergence, and lets you push or pull changes. + +### Validate before sharing + +``` +/harness-validate +/harness-validate path/to/harness.yaml +``` + +## Output Format + +```yaml +$schema: https://harnessprotocol.io/schema/v1/harness.schema.json +version: "1" + +metadata: + name: my-harness + description: My personal harness configuration. + +plugins: + - name: explain + source: harnessprotocol/harness-kit + description: Layered explanations of files, functions, directories, or concepts +``` + +## Shell Alternative + +If you don't have Claude Code, use `harness-restore.sh` instead: + +```bash +curl -fsSL https://raw.githubusercontent.com/harnessprotocol/harness-kit/main/harness-restore.sh | bash -s -- harness.yaml +``` + +## Runtime Configuration — harness-export + +{/* @embed plugins/harness-share/skills/harness-export/SKILL.md */} + +## Runtime Configuration — harness-import + +{/* @embed plugins/harness-share/skills/harness-import/SKILL.md */} + +## Runtime Configuration — harness-compile + +{/* @embed plugins/harness-share/skills/harness-compile/SKILL.md */} + +## Runtime Configuration — harness-sync + +{/* @embed plugins/harness-share/skills/harness-sync/SKILL.md */} + +## Runtime Configuration — harness-validate + +{/* @embed plugins/harness-share/skills/harness-validate/SKILL.md */} + +## Plugin Documentation — harness-export + +{/* @embed plugins/harness-share/skills/harness-export/README.md */} + +## Plugin Documentation — harness-import + +{/* @embed plugins/harness-share/skills/harness-import/README.md */} + +## Plugin Documentation — harness-compile + +{/* @embed plugins/harness-share/skills/harness-compile/README.md */} + +## Plugin Documentation — harness-sync + +{/* @embed plugins/harness-share/skills/harness-sync/README.md */} + +## Plugin Documentation — harness-validate + +{/* @embed plugins/harness-share/skills/harness-validate/README.md */} diff --git a/website/content/docs/plugins/productivity/meta.json b/website/content/docs/plugins/productivity/meta.json new file mode 100644 index 0000000..acefd59 --- /dev/null +++ b/website/content/docs/plugins/productivity/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Productivity", + "pages": ["harness-share"] +} diff --git a/website/content/docs/plugins/capture-session.md b/website/content/docs/plugins/research-knowledge/capture-session.mdx similarity index 85% rename from website/content/docs/plugins/capture-session.md rename to website/content/docs/plugins/research-knowledge/capture-session.mdx index d92557c..82f2535 100644 --- a/website/content/docs/plugins/capture-session.md +++ b/website/content/docs/plugins/research-knowledge/capture-session.mdx @@ -3,6 +3,8 @@ sidebar_position: 6 title: capture-session --- +> **[View in Marketplace →](https://marketplace.harnessprotocol.io/plugins/capture-session)** for install tracking, related plugins, and profiles. + # capture-session Capture session information into a staging file for later reflection and knowledge graph processing. @@ -82,6 +84,8 @@ The Stop hook automatically summarizes sessions at exit. Add to `~/.claude/hooks } ``` +The `"matcher": ""` field is an empty string, which means the hook fires on every Stop event regardless of context. To restrict it to specific projects, set `matcher` to a glob pattern matching the project path. + After `plugin install capture-session@harness-kit`, the script is at `~/.claude/plugins/capture-session/scripts/session-capture.sh`. For a local clone, use the absolute path to `plugins/capture-session/scripts/session-capture.sh`. The hook reads the Stop event payload, finds the session transcript, summarizes it, and appends to the staging file. It deduplicates against `` entries from the same day. @@ -125,3 +129,11 @@ Both sources feed the same staging file, processed uniformly by the daily reflec ### Why append-only? The staging file is a write-ahead log. Modifying existing entries would corrupt the audit trail and break deduplication logic. + +## Runtime Configuration + +{/* @embed plugins/capture-session/skills/capture-session/SKILL.md */} + +## Plugin Documentation + +{/* @embed plugins/capture-session/skills/capture-session/README.md */} diff --git a/website/content/docs/plugins/research-knowledge/meta.json b/website/content/docs/plugins/research-knowledge/meta.json new file mode 100644 index 0000000..1c28173 --- /dev/null +++ b/website/content/docs/plugins/research-knowledge/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Research & Knowledge", + "pages": ["research", "orient", "capture-session"] +} diff --git a/website/content/docs/plugins/orient.mdx b/website/content/docs/plugins/research-knowledge/orient.mdx similarity index 90% rename from website/content/docs/plugins/orient.mdx rename to website/content/docs/plugins/research-knowledge/orient.mdx index 593d2fd..306ab7b 100644 --- a/website/content/docs/plugins/orient.mdx +++ b/website/content/docs/plugins/research-knowledge/orient.mdx @@ -3,6 +3,8 @@ sidebar_position: 5 title: orient --- +> **[View in Marketplace →](https://marketplace.harnessprotocol.io/plugins/orient)** for install tracking, related plugins, and profiles. + # orient Topic-focused session orientation across graph, knowledge, journal, and research. @@ -101,3 +103,11 @@ Session orientation is all-or-nothing — you either load everything or manually ### Why strict caps? Orientation should be fast. Dumping 50 graph entities defeats the purpose. The caps keep output small enough to orient, not enough to overwhelm. + +## Runtime Configuration + +{/* @embed plugins/orient/skills/orient/SKILL.md */} + +## Plugin Documentation + +{/* @embed plugins/orient/skills/orient/README.md */} diff --git a/website/content/docs/plugins/research.md b/website/content/docs/plugins/research-knowledge/research.mdx similarity index 95% rename from website/content/docs/plugins/research.md rename to website/content/docs/plugins/research-knowledge/research.mdx index 3205f97..c63ad84 100644 --- a/website/content/docs/plugins/research.md +++ b/website/content/docs/plugins/research-knowledge/research.mdx @@ -3,6 +3,8 @@ sidebar_position: 2 title: research --- +> **[View in Marketplace →](https://marketplace.harnessprotocol.io/plugins/research)** for install tracking, related plugins, and profiles. + # research Process any source into a structured, compounding knowledge base. @@ -145,3 +147,11 @@ Research on the same subject from different angles should compound. Separate syn ### Why the INDEX.md? `research/INDEX.md` is a flat table of every synthesized topic. It makes the whole knowledge base scannable at a glance and is the first thing checked before fetching anything new — fast duplicate detection without scanning every file. + +## Runtime Configuration + +{/* @embed plugins/research/skills/research/SKILL.md */} + +## Plugin Documentation + +{/* @embed plugins/research/skills/research/README.md */} diff --git a/website/lib/remark-embed.mjs b/website/lib/remark-embed.mjs new file mode 100644 index 0000000..7779259 --- /dev/null +++ b/website/lib/remark-embed.mjs @@ -0,0 +1,83 @@ +import { visit } from 'unist-util-visit'; +import fs from 'node:fs'; +import path from 'node:path'; +import { fromMarkdown } from 'mdast-util-from-markdown'; + +// Remark plugin that replaces @embed directives with file contents at build time. +// +// Usage in MDX: +// {/* @embed plugins/research/skills/research/SKILL.md */} +// +// The directive is replaced with the parsed markdown AST of the referenced file. +// Paths are resolved relative to a configurable root (defaults to the repo root, +// determined by walking up from process.cwd() to find a .git directory). +export function remarkEmbed({ root } = {}) { + return (tree, file) => { + const resolvedRoot = root || findRepoRoot(process.cwd()); + + // Collect nodes to replace (avoid mutating during traversal) + const replacements = []; + + visit(tree, (node, index, parent) => { + if ( + node.type !== 'mdxFlowExpression' && + node.type !== 'mdxTextExpression' + ) { + return; + } + + const match = node.value?.match( + /\/\*\s*@embed\s+(.+?)\s*\*\// + ); + if (!match) return; + + const relativePath = match[1].trim(); + const absolutePath = path.resolve(resolvedRoot, relativePath); + + if (!fs.existsSync(absolutePath)) { + console.warn( + `[remark-embed] File not found: ${absolutePath} (referenced in ${file.path || 'unknown'})` + ); + return; + } + + let content; + try { + content = fs.readFileSync(absolutePath, 'utf-8'); + } catch (err) { + console.warn( + `[remark-embed] Could not read ${absolutePath}: ${err.message}` + ); + return; + } + + // Parse the markdown content into AST nodes + const embeddedTree = fromMarkdown(content); + + replacements.push({ parent, index, nodes: embeddedTree.children }); + }); + + // Apply replacements in reverse order to preserve indices + for (let i = replacements.length - 1; i >= 0; i--) { + const { parent, index, nodes } = replacements[i]; + parent.children.splice(index, 1, ...nodes); + } + }; +} + +/** + * Walk up from startDir looking for a .git directory to find the repo root. + * Falls back to startDir if no .git is found. + */ +function findRepoRoot(startDir) { + let dir = startDir; + while (dir !== path.dirname(dir)) { + if (fs.existsSync(path.join(dir, '.git'))) { + return dir; + } + dir = path.dirname(dir); + } + return startDir; +} + +export default remarkEmbed; diff --git a/website/package.json b/website/package.json index 0055d54..6be7aae 100644 --- a/website/package.json +++ b/website/package.json @@ -12,17 +12,19 @@ "fumadocs-core": "^15.0.0", "fumadocs-mdx": "^11.0.0", "fumadocs-ui": "^15.0.0", + "mdast-util-from-markdown": "^2.0.3", "next": "^15.0.0", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "unist-util-visit": "^5.1.0" }, "devDependencies": { + "@tailwindcss/postcss": "^4.0.0", "@types/node": "^22.0.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", - "typescript": "^5.0.0", + "postcss": "^8.0.0", "tailwindcss": "^4.0.0", - "@tailwindcss/postcss": "^4.0.0", - "postcss": "^8.0.0" + "typescript": "^5.0.0" } } diff --git a/website/source.config.ts b/website/source.config.ts index 7784ccb..70cbb73 100644 --- a/website/source.config.ts +++ b/website/source.config.ts @@ -1,9 +1,12 @@ import { defineDocs, defineConfig } from 'fumadocs-mdx/config'; +import { remarkEmbed } from './lib/remark-embed.mjs'; export const docs = defineDocs({ dir: 'content/docs', }); export default defineConfig({ - mdxOptions: {}, + mdxOptions: { + remarkPlugins: [remarkEmbed], + }, });