diff --git a/code-reference-finder/.env.example b/code-reference-finder/.env.example
new file mode 100644
index 0000000..4c41230
--- /dev/null
+++ b/code-reference-finder/.env.example
@@ -0,0 +1,4 @@
+OPENROUTER_API_KEY=your_openrouter_api_key
+TINYFISH_API_KEY=your_tinyfish_api_key
+GITHUB_TOKEN=your_github_personal_access_token
+STACKEXCHANGE_KEY=your_stackexchange_api_key
diff --git a/code-reference-finder/.gitignore b/code-reference-finder/.gitignore
new file mode 100644
index 0000000..5ef6a52
--- /dev/null
+++ b/code-reference-finder/.gitignore
@@ -0,0 +1,41 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.*
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/versions
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# env files (can opt-in for committing if needed)
+.env*
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/code-reference-finder/README.md b/code-reference-finder/README.md
new file mode 100644
index 0000000..33a654c
--- /dev/null
+++ b/code-reference-finder/README.md
@@ -0,0 +1,151 @@
+# Code Reference Finder
+
+**Live:** [https://code-reference-finder.vercel.app](https://code-reference-finder.vercel.app)
+
+Code Reference Finder helps you understand unfamiliar code by finding real-world usage examples from GitHub repositories and Stack Overflow. Paste a code snippet (or right-click selected code on GitHub), and it uses AI to analyze the libraries and APIs used, then dispatches web agents to search GitHub and Stack Overflow, extract relevant examples, and display them side-by-side with relevance scores.
+
+## Demo
+
+https://github.com/user-attachments/assets/73feb7c2-60dd-492b-b440-165d0170a4aa
+
+
+## TinyFish API Usage
+
+The app dispatches 10 parallel TinyFish web agents — 5 for GitHub repos and 5 for Stack Overflow posts. Each agent receives a goal prompt tailored to its platform.
+
+**GitHub agents** navigate the repository and read the README to extract relevant code examples:
+
+```typescript
+const response = await fetch("https://agent.tinyfish.ai/v1/automation/run-sse", {
+ method: "POST",
+ headers: {
+ "X-API-Key": process.env.TINYFISH_API_KEY,
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ url: "https://github.com/owner/repo",
+ goal: `You are analyzing a GitHub repository to determine how it relates to specific libraries and APIs.
+
+ TARGET LIBRARIES: @tanstack/react-query, axios
+ TARGET APIs/SYMBOLS: useQuery, axios.get
+
+ INSTRUCTIONS:
+ 1. Go to the repository page and read ONLY the README.
+ 2. Extract: what the project does, any code examples shown, and how it relates to the target libraries/APIs.
+ 3. Score relevance 0-100.
+
+ Return a JSON object with: title, sourceUrl, platform, relevanceScore, alignmentExplanation, codeSnippets...`,
+ }),
+});
+```
+
+**Stack Overflow agents** reason over the post metadata (title, tags, score, excerpt) without navigating:
+
+```typescript
+const response = await fetch("https://agent.tinyfish.ai/v1/automation/run-sse", {
+ method: "POST",
+ headers: {
+ "X-API-Key": process.env.TINYFISH_API_KEY,
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ url: "https://example.com",
+ goal: `You are a reasoning agent analyzing a Stack Overflow post.
+
+ STACK OVERFLOW POST DATA:
+ - Title: How to pass parameters to useQuery with Axios
+ - Score: 38 | Answered: true | Tags: reactjs, axios, react-query
+
+ TARGET LIBRARIES: @tanstack/react-query, axios
+
+ Score relevance 0-100 based on: Do the tags match? Does the title discuss the target APIs?
+ Would this post help someone understand how to use these libraries?
+
+ Return a JSON object with: title, sourceUrl, platform, relevanceScore, alignmentExplanation, questionTitle, votes, tags...`,
+ }),
+});
+```
+
+Both agent types stream SSE events including a `STREAMING_URL` (live view of the agent working) and a final `COMPLETE` event with the extracted reference data JSON.
+
+## How to Run
+
+### Prerequisites
+
+- Node.js 18+
+- API keys for: [OpenRouter](https://openrouter.ai/keys), [TinyFish](https://mino.ai/api-keys), [GitHub](https://github.com/settings/tokens), and [Stack Exchange](https://stackapps.com/apps/oauth/register)
+
+### Setup
+
+1. Install dependencies:
+
+```bash
+npm install
+```
+
+2. Create a `.env.local` file with your API keys (see `.env.example`):
+
+```
+OPENROUTER_API_KEY=your_openrouter_api_key
+TINYFISH_API_KEY=your_tinyfish_api_key
+GITHUB_TOKEN=your_github_personal_access_token
+STACKEXCHANGE_KEY=your_stackexchange_api_key
+```
+
+3. Start the dev server:
+
+```bash
+npm run dev
+```
+
+4. Open [http://localhost:3000](http://localhost:3000)
+
+### Chrome Extension (optional)
+
+To use the side panel and right-click context menu on GitHub:
+
+1. Go to `chrome://extensions` and enable Developer mode
+2. Click "Load unpacked" and select the `extension/` folder
+3. Copy any unknown code and paste it in the input area to start using it.
+
+## Architecture Diagram
+
+```
+┌──────────────────────────────────────────────────────────────────┐
+│ User (Browser) │
+│ ┌────────────────────────────────────────────────────────────┐ │
+│ │ Next.js Frontend (React + Tailwind + Framer Motion) │ │
+│ │ │ │
+│ │ 1. Paste code snippet or right-click on GitHub │ │
+│ │ 2. View analysis (language, libraries, APIs, patterns) │ │
+│ │ 3. Watch agents search & extract in real-time │ │
+│ │ 4. Browse results sorted by relevance score │ │
+│ └───────────────────────┬────────────────────────────────────┘ │
+└──────────────────────────┼───────────────────────────────────────┘
+ │ POST /api/analyze (SSE stream)
+ ▼
+┌──────────────────────────────────────────────────────────────────┐
+│ Next.js API Route (SSE) │
+│ │
+│ Stage 1 — Code Analysis (OpenRouter / Gemini Flash) │
+│ • Identifies language, libraries, APIs, patterns │
+│ • Generates 10 search queries (5 GitHub + 5 Stack Overflow) │
+│ │
+│ Stage 2 — Search Execution │
+│ • GitHub Search API (5 queries, rate-limited) │
+│ • Stack Exchange API (5 queries, parallel) │
+│ • Deduplicates and picks top 5 from each platform │
+│ │
+│ Stage 3 — Agent Extraction (10 parallel TinyFish agents) │
+│ • GitHub agents: navigate repo, read README, extract examples │
+│ • SO agents: reason over post metadata, score relevance │
+│ │
+│ Stage 4 — Pipeline Complete │
+└────────┬──────────────┬──────────────┬──────────────┬────────────┘
+ │ │ │ │
+ ▼ ▼ ▼ ▼
+ ┌──────────┐ ┌──────────┐ ┌───────────┐ ┌───────────┐
+ │ OpenRouter│ │ GitHub │ │ Stack │ │ TinyFish │
+ │ (LLM) │ │ API │ │ Exchange │ │ (Agents) │
+ └──────────┘ └──────────┘ └───────────┘ └───────────┘
+```
diff --git a/code-reference-finder/extension/background.js b/code-reference-finder/extension/background.js
new file mode 100644
index 0000000..182f25f
--- /dev/null
+++ b/code-reference-finder/extension/background.js
@@ -0,0 +1,25 @@
+// Open side panel when extension icon is clicked
+chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true });
+
+// Create context menu for selected code
+chrome.runtime.onInstalled.addListener(() => {
+ chrome.contextMenus.create({
+ id: 'find-references',
+ title: 'Find References for Selected Code',
+ contexts: ['selection'],
+ });
+});
+
+// Handle context menu clicks — this IS a valid user gesture, so sidePanel.open works
+chrome.contextMenus.onClicked.addListener((info, tab) => {
+ if (info.menuItemId === 'find-references' && info.selectionText && tab?.id) {
+ // Store code first
+ chrome.storage.local.set({
+ pendingCode: info.selectionText,
+ pendingTimestamp: Date.now(),
+ });
+
+ // Open side panel (allowed from context menu handler)
+ chrome.sidePanel.open({ tabId: tab.id });
+ }
+});
diff --git a/code-reference-finder/extension/manifest.json b/code-reference-finder/extension/manifest.json
new file mode 100644
index 0000000..0092cef
--- /dev/null
+++ b/code-reference-finder/extension/manifest.json
@@ -0,0 +1,16 @@
+{
+ "manifest_version": 3,
+ "name": "Code Reference Finder",
+ "version": "1.0.0",
+ "description": "Find real-world usage examples for unfamiliar code from GitHub and Stack Overflow",
+ "permissions": ["sidePanel", "contextMenus", "storage"],
+ "side_panel": {
+ "default_path": "sidepanel.html"
+ },
+ "background": {
+ "service_worker": "background.js"
+ },
+ "action": {
+ "default_title": "Code Reference Finder"
+ }
+}
diff --git a/code-reference-finder/extension/sidepanel.html b/code-reference-finder/extension/sidepanel.html
new file mode 100644
index 0000000..2261899
--- /dev/null
+++ b/code-reference-finder/extension/sidepanel.html
@@ -0,0 +1,22 @@
+
+
+
+
+
+ Code Reference Finder
+
+
+
+
+
+
+
+
diff --git a/code-reference-finder/extension/sidepanel.js b/code-reference-finder/extension/sidepanel.js
new file mode 100644
index 0000000..88520f8
--- /dev/null
+++ b/code-reference-finder/extension/sidepanel.js
@@ -0,0 +1,31 @@
+const appFrame = document.getElementById('app-frame');
+let lastTimestamp = 0;
+
+function injectCode(code) {
+ appFrame.contentWindow.postMessage(
+ { type: 'INJECT_CODE', code: code },
+ '*'
+ );
+}
+
+// Check for pending code on load (side panel just opened)
+appFrame.addEventListener('load', () => {
+ chrome.storage.local.get(['pendingCode', 'pendingTimestamp'], (data) => {
+ if (data.pendingCode && data.pendingTimestamp > lastTimestamp) {
+ lastTimestamp = data.pendingTimestamp;
+ // Small delay to ensure the Next.js app is ready
+ setTimeout(() => injectCode(data.pendingCode), 500);
+ }
+ });
+});
+
+// Watch for new code stored by content script or background
+chrome.storage.onChanged.addListener((changes) => {
+ if (changes.pendingCode && changes.pendingTimestamp) {
+ const newTimestamp = changes.pendingTimestamp.newValue;
+ if (newTimestamp > lastTimestamp) {
+ lastTimestamp = newTimestamp;
+ injectCode(changes.pendingCode.newValue);
+ }
+ }
+});
diff --git a/code-reference-finder/next.config.ts b/code-reference-finder/next.config.ts
new file mode 100644
index 0000000..8cea48e
--- /dev/null
+++ b/code-reference-finder/next.config.ts
@@ -0,0 +1,20 @@
+import type { NextConfig } from 'next';
+
+const nextConfig: NextConfig = {
+ async headers() {
+ return [
+ {
+ // Allow Chrome extension to embed this app in an iframe
+ source: '/(.*)',
+ headers: [
+ {
+ key: 'Content-Security-Policy',
+ value: "frame-ancestors 'self' chrome-extension://*",
+ },
+ ],
+ },
+ ];
+ },
+};
+
+export default nextConfig;
diff --git a/code-reference-finder/package-lock.json b/code-reference-finder/package-lock.json
new file mode 100644
index 0000000..250d74a
--- /dev/null
+++ b/code-reference-finder/package-lock.json
@@ -0,0 +1,1717 @@
+{
+ "name": "code-ref-temp",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "code-ref-temp",
+ "version": "0.1.0",
+ "dependencies": {
+ "framer-motion": "^12.34.0",
+ "lucide-react": "^0.563.0",
+ "next": "16.1.6",
+ "react": "19.2.3",
+ "react-dom": "19.2.3"
+ },
+ "devDependencies": {
+ "@tailwindcss/postcss": "^4",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "tailwindcss": "^4",
+ "typescript": "^5"
+ }
+ },
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
+ "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@img/colour": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz",
+ "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
+ "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
+ "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
+ "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
+ "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
+ "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
+ "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
+ "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-riscv64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
+ "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
+ "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
+ "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
+ "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
+ "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
+ "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
+ "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
+ "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-riscv64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
+ "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-riscv64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
+ "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
+ "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
+ "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
+ "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
+ "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.7.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
+ "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
+ "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
+ "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@next/env": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.6.tgz",
+ "integrity": "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==",
+ "license": "MIT"
+ },
+ "node_modules/@next/swc-darwin-arm64": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.6.tgz",
+ "integrity": "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-darwin-x64": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.6.tgz",
+ "integrity": "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-gnu": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.6.tgz",
+ "integrity": "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-musl": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.6.tgz",
+ "integrity": "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-gnu": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.6.tgz",
+ "integrity": "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-musl": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.6.tgz",
+ "integrity": "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-arm64-msvc": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.6.tgz",
+ "integrity": "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-x64-msvc": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.6.tgz",
+ "integrity": "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.15",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
+ "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@tailwindcss/node": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz",
+ "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/remapping": "^2.3.4",
+ "enhanced-resolve": "^5.18.3",
+ "jiti": "^2.6.1",
+ "lightningcss": "1.30.2",
+ "magic-string": "^0.30.21",
+ "source-map-js": "^1.2.1",
+ "tailwindcss": "4.1.18"
+ }
+ },
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz",
+ "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.1.18",
+ "@tailwindcss/oxide-darwin-arm64": "4.1.18",
+ "@tailwindcss/oxide-darwin-x64": "4.1.18",
+ "@tailwindcss/oxide-freebsd-x64": "4.1.18",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.1.18",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.1.18",
+ "@tailwindcss/oxide-linux-x64-musl": "4.1.18",
+ "@tailwindcss/oxide-wasm32-wasi": "4.1.18",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.1.18"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-android-arm64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz",
+ "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-arm64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz",
+ "integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-x64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz",
+ "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-freebsd-x64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz",
+ "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz",
+ "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz",
+ "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz",
+ "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz",
+ "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-musl": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz",
+ "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz",
+ "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==",
+ "bundleDependencies": [
+ "@napi-rs/wasm-runtime",
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@tybys/wasm-util",
+ "@emnapi/wasi-threads",
+ "tslib"
+ ],
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1",
+ "@emnapi/wasi-threads": "^1.1.0",
+ "@napi-rs/wasm-runtime": "^1.1.0",
+ "@tybys/wasm-util": "^0.10.1",
+ "tslib": "^2.4.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz",
+ "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz",
+ "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/postcss": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.18.tgz",
+ "integrity": "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "@tailwindcss/node": "4.1.18",
+ "@tailwindcss/oxide": "4.1.18",
+ "postcss": "^8.4.41",
+ "tailwindcss": "4.1.18"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.19.33",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.33.tgz",
+ "integrity": "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.13",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.13.tgz",
+ "integrity": "sha512-KkiJeU6VbYbUOp5ITMIc7kBfqlYkKA5KhEHVrGMmUUMt7NeaZg65ojdPk+FtNrBAOXNVM5QM72jnADjM+XVRAQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.9.19",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz",
+ "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==",
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.js"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001769",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz",
+ "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/client-only": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
+ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
+ "license": "MIT"
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "devOptional": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.19.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz",
+ "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.3.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/framer-motion": {
+ "version": "12.34.0",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.34.0.tgz",
+ "integrity": "sha512-+/H49owhzkzQyxtn7nZeF4kdH++I2FWrESQ184Zbcw5cEqNHYkE5yxWxcTLSj5lNx3NWdbIRy5FHqUvetD8FWg==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-dom": "^12.34.0",
+ "motion-utils": "^12.29.2",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/jiti": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
+ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
+ "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.30.2",
+ "lightningcss-darwin-arm64": "1.30.2",
+ "lightningcss-darwin-x64": "1.30.2",
+ "lightningcss-freebsd-x64": "1.30.2",
+ "lightningcss-linux-arm-gnueabihf": "1.30.2",
+ "lightningcss-linux-arm64-gnu": "1.30.2",
+ "lightningcss-linux-arm64-musl": "1.30.2",
+ "lightningcss-linux-x64-gnu": "1.30.2",
+ "lightningcss-linux-x64-musl": "1.30.2",
+ "lightningcss-win32-arm64-msvc": "1.30.2",
+ "lightningcss-win32-x64-msvc": "1.30.2"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz",
+ "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz",
+ "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz",
+ "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz",
+ "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz",
+ "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz",
+ "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz",
+ "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz",
+ "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz",
+ "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz",
+ "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz",
+ "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lucide-react": {
+ "version": "0.563.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.563.0.tgz",
+ "integrity": "sha512-8dXPB2GI4dI8jV4MgUDGBeLdGk8ekfqVZ0BdLcrRzocGgG75ltNEmWS+gE7uokKF/0oSUuczNDT+g9hFJ23FkA==",
+ "license": "ISC",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/motion-dom": {
+ "version": "12.34.0",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.34.0.tgz",
+ "integrity": "sha512-Lql3NuEcScRDxTAO6GgUsRHBZOWI/3fnMlkMcH5NftzcN37zJta+bpbMAV9px4Nj057TuvRooMK7QrzMCgtz6Q==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-utils": "^12.29.2"
+ }
+ },
+ "node_modules/motion-utils": {
+ "version": "12.29.2",
+ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.29.2.tgz",
+ "integrity": "sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/next": {
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/next/-/next-16.1.6.tgz",
+ "integrity": "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==",
+ "license": "MIT",
+ "dependencies": {
+ "@next/env": "16.1.6",
+ "@swc/helpers": "0.5.15",
+ "baseline-browser-mapping": "^2.8.3",
+ "caniuse-lite": "^1.0.30001579",
+ "postcss": "8.4.31",
+ "styled-jsx": "5.1.6"
+ },
+ "bin": {
+ "next": "dist/bin/next"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "optionalDependencies": {
+ "@next/swc-darwin-arm64": "16.1.6",
+ "@next/swc-darwin-x64": "16.1.6",
+ "@next/swc-linux-arm64-gnu": "16.1.6",
+ "@next/swc-linux-arm64-musl": "16.1.6",
+ "@next/swc-linux-x64-gnu": "16.1.6",
+ "@next/swc-linux-x64-musl": "16.1.6",
+ "@next/swc-win32-arm64-msvc": "16.1.6",
+ "@next/swc-win32-x64-msvc": "16.1.6",
+ "sharp": "^0.34.4"
+ },
+ "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
+ }
+ }
+ },
+ "node_modules/next/node_modules/postcss": {
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
+ "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.3"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "license": "ISC",
+ "optional": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/sharp": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
+ "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@img/colour": "^1.0.0",
+ "detect-libc": "^2.1.2",
+ "semver": "^7.7.3"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "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"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/styled-jsx": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
+ "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
+ "license": "MIT",
+ "dependencies": {
+ "client-only": "0.0.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "peerDependencies": {
+ "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
+ }
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
+ "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tapable": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
+ "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ }
+ }
+}
diff --git a/code-reference-finder/package.json b/code-reference-finder/package.json
new file mode 100644
index 0000000..ae3c38a
--- /dev/null
+++ b/code-reference-finder/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "code-reference-finder",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start"
+ },
+ "dependencies": {
+ "framer-motion": "^12.34.0",
+ "lucide-react": "^0.563.0",
+ "next": "16.1.6",
+ "react": "19.2.3",
+ "react-dom": "19.2.3"
+ },
+ "devDependencies": {
+ "@tailwindcss/postcss": "^4",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "tailwindcss": "^4",
+ "typescript": "^5"
+ }
+}
diff --git a/code-reference-finder/postcss.config.mjs b/code-reference-finder/postcss.config.mjs
new file mode 100644
index 0000000..61e3684
--- /dev/null
+++ b/code-reference-finder/postcss.config.mjs
@@ -0,0 +1,7 @@
+const config = {
+ plugins: {
+ "@tailwindcss/postcss": {},
+ },
+};
+
+export default config;
diff --git a/code-reference-finder/src/app/api/analyze/route.ts b/code-reference-finder/src/app/api/analyze/route.ts
new file mode 100644
index 0000000..be8dcee
--- /dev/null
+++ b/code-reference-finder/src/app/api/analyze/route.ts
@@ -0,0 +1,47 @@
+import { NextRequest, NextResponse } from 'next/server';
+import { runPipeline } from '@/lib/orchestrator';
+
+export async function POST(request: NextRequest) {
+ try {
+ const body = await request.json();
+ const code = body?.code;
+
+ if (!code || typeof code !== 'string' || code.trim().length === 0) {
+ return NextResponse.json(
+ { error: 'Missing or empty "code" field in request body' },
+ { status: 400 }
+ );
+ }
+
+ // Create a TransformStream for SSE
+ const { readable, writable } = new TransformStream();
+ const writer = writable.getWriter();
+
+ // Run the pipeline in the background — don't await
+ runPipeline(code.trim(), writer)
+ .catch((err) => {
+ const encoder = new TextEncoder();
+ const errorEvent = `data: ${JSON.stringify({
+ type: 'pipeline_error',
+ data: { error: (err as Error).message },
+ })}\n\n`;
+ writer.write(encoder.encode(errorEvent)).catch(() => {});
+ })
+ .finally(() => {
+ writer.close().catch(() => {});
+ });
+
+ return new Response(readable, {
+ headers: {
+ 'Content-Type': 'text/event-stream',
+ 'Cache-Control': 'no-cache',
+ Connection: 'keep-alive',
+ },
+ });
+ } catch (error) {
+ return NextResponse.json(
+ { error: (error as Error).message },
+ { status: 500 }
+ );
+ }
+}
diff --git a/code-reference-finder/src/app/favicon.ico b/code-reference-finder/src/app/favicon.ico
new file mode 100644
index 0000000..718d6fe
Binary files /dev/null and b/code-reference-finder/src/app/favicon.ico differ
diff --git a/code-reference-finder/src/app/globals.css b/code-reference-finder/src/app/globals.css
new file mode 100644
index 0000000..b2ef401
--- /dev/null
+++ b/code-reference-finder/src/app/globals.css
@@ -0,0 +1,37 @@
+@import "tailwindcss";
+
+:root {
+ --background: #09090b;
+ --foreground: #fafafa;
+}
+
+@theme inline {
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --font-sans: var(--font-geist-sans);
+ --font-mono: var(--font-geist-mono);
+}
+
+body {
+ background: var(--background);
+ color: var(--foreground);
+ font-family: var(--font-sans), system-ui, -apple-system, sans-serif;
+}
+
+/* Scrollbar styling */
+::-webkit-scrollbar {
+ width: 6px;
+}
+
+::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+::-webkit-scrollbar-thumb {
+ background: #3f3f46;
+ border-radius: 3px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: #52525b;
+}
diff --git a/code-reference-finder/src/app/layout.tsx b/code-reference-finder/src/app/layout.tsx
new file mode 100644
index 0000000..e172add
--- /dev/null
+++ b/code-reference-finder/src/app/layout.tsx
@@ -0,0 +1,36 @@
+import type { Metadata } from 'next';
+import { Geist, Geist_Mono } from 'next/font/google';
+import './globals.css';
+import { AppProvider } from '@/context/AppContext';
+
+const geistSans = Geist({
+ variable: '--font-geist-sans',
+ subsets: ['latin'],
+});
+
+const geistMono = Geist_Mono({
+ variable: '--font-geist-mono',
+ subsets: ['latin'],
+});
+
+export const metadata: Metadata = {
+ title: 'Code Reference Finder',
+ description:
+ 'Find real-world usage examples for unfamiliar code from GitHub and Stack Overflow',
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/code-reference-finder/src/app/page.tsx b/code-reference-finder/src/app/page.tsx
new file mode 100644
index 0000000..42aafe4
--- /dev/null
+++ b/code-reference-finder/src/app/page.tsx
@@ -0,0 +1,39 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+import { Header } from '@/components/Header';
+import { CodeInput } from '@/components/CodeInput';
+import { Dashboard } from '@/components/Dashboard';
+import { useCodeAnalysis } from '@/hooks/useCodeAnalysis';
+
+export default function Home() {
+ const { analyze, cancel, reset, state } = useCodeAnalysis();
+ const [injectedCode, setInjectedCode] = useState(null);
+
+ // Listen for postMessage from Chrome extension iframe
+ useEffect(() => {
+ const handler = (event: MessageEvent) => {
+ if (event.data?.type === 'INJECT_CODE' && event.data.code) {
+ setInjectedCode(event.data.code);
+ }
+ };
+ window.addEventListener('message', handler);
+ return () => window.removeEventListener('message', handler);
+ }, []);
+
+ const isInputPhase = state.phase === 'input';
+
+ return (
+
+
+ {isInputPhase ? (
+
+ ) : (
+
+ )}
+
+ );
+}
diff --git a/code-reference-finder/src/components/AgentCard.tsx b/code-reference-finder/src/components/AgentCard.tsx
new file mode 100644
index 0000000..bfc3677
--- /dev/null
+++ b/code-reference-finder/src/components/AgentCard.tsx
@@ -0,0 +1,119 @@
+'use client';
+
+import { useState, useEffect } from 'react';
+import { motion } from 'framer-motion';
+import { Github, MessageCircle, Loader2, AlertCircle, ChevronDown, ChevronUp } from 'lucide-react';
+import { MiniPreview } from './LiveBrowserPreview';
+import type { ReferenceAgentState } from '@/lib/types';
+
+interface AgentCardProps {
+ agent: ReferenceAgentState;
+ onPreviewClick: (streamingUrl: string, title: string) => void;
+}
+
+export function AgentCard({ agent, onPreviewClick }: AgentCardProps) {
+ const [showSteps, setShowSteps] = useState(false);
+ const [elapsed, setElapsed] = useState(0);
+
+ useEffect(() => {
+ if (agent.status === 'complete' || agent.status === 'error') return;
+ if (!agent.startedAt) return;
+
+ const interval = setInterval(() => {
+ setElapsed(Math.floor((Date.now() - agent.startedAt!) / 1000));
+ }, 1000);
+
+ return () => clearInterval(interval);
+ }, [agent.status, agent.startedAt]);
+
+ const isGitHub = agent.platform === 'github';
+ const statusColor =
+ agent.status === 'error'
+ ? 'text-red-400'
+ : agent.status === 'complete'
+ ? 'text-green-400'
+ : 'text-blue-400';
+
+ return (
+
+ {/* Header */}
+
+
+ {isGitHub ? (
+
+ ) : (
+
+ )}
+
+ {agent.url.replace('https://', '')}
+
+
+
+ {agent.status !== 'complete' && agent.status !== 'error' && (
+
+ )}
+ {agent.status === 'error' &&
}
+
{agent.status}
+
+
+
+ {/* Current step */}
+
+ {agent.currentStep}
+
+
+ {/* Elapsed time */}
+ {agent.startedAt && (
+
+ {agent.completedAt
+ ? `Completed in ${Math.floor((agent.completedAt - agent.startedAt) / 1000)}s`
+ : `${elapsed}s elapsed`}
+
+ )}
+
+ {/* Error message */}
+ {agent.error && (
+
+ {agent.error}
+
+ )}
+
+ {/* Mini preview */}
+ {agent.streamingUrl && agent.status !== 'complete' && agent.status !== 'error' && (
+ onPreviewClick(agent.streamingUrl!, agent.url)}
+ />
+ )}
+
+ {/* Step history toggle */}
+ {agent.steps.length > 0 && (
+
+ )}
+
+ {showSteps && (
+
+ {agent.steps.map((step, i) => (
+
+ {step.message}
+
+ ))}
+
+ )}
+
+ );
+}
diff --git a/code-reference-finder/src/components/AnalysisSummary.tsx b/code-reference-finder/src/components/AnalysisSummary.tsx
new file mode 100644
index 0000000..3ac1dc2
--- /dev/null
+++ b/code-reference-finder/src/components/AnalysisSummary.tsx
@@ -0,0 +1,75 @@
+'use client';
+
+import { Code2, Package, Puzzle, Workflow } from 'lucide-react';
+import type { CodeAnalysis } from '@/lib/types';
+
+interface AnalysisSummaryProps {
+ analysis: CodeAnalysis;
+}
+
+export function AnalysisSummary({ analysis }: AnalysisSummaryProps) {
+ return (
+
+
+
+
+ Language:
+
+ {analysis.language}
+
+
+
+ {analysis.libraries.length > 0 && (
+
+
+
Libraries:
+
+ {analysis.libraries.map((lib) => (
+
+ {lib}
+
+ ))}
+
+
+ )}
+
+ {analysis.apis.length > 0 && (
+
+
+
APIs:
+
+ {analysis.apis.map((api) => (
+
+ {api}
+
+ ))}
+
+
+ )}
+
+ {analysis.patterns.length > 0 && (
+
+
+
Patterns:
+
+ {analysis.patterns.map((p) => (
+
+ {p}
+
+ ))}
+
+
+ )}
+
+
+ );
+}
diff --git a/code-reference-finder/src/components/CodeInput.tsx b/code-reference-finder/src/components/CodeInput.tsx
new file mode 100644
index 0000000..3602ad1
--- /dev/null
+++ b/code-reference-finder/src/components/CodeInput.tsx
@@ -0,0 +1,132 @@
+'use client';
+
+import { useState } from 'react';
+import { Search, Clipboard } from 'lucide-react';
+
+interface CodeInputProps {
+ onSubmit: (code: string) => void;
+ injectedCode?: string | null;
+}
+
+const EXAMPLES = [
+ {
+ label: 'React Query + Axios',
+ code: `import { useQuery } from '@tanstack/react-query';
+import axios from 'axios';
+
+const { data, isLoading } = useQuery(['users'], () =>
+ axios.get('/api/users').then(res => res.data)
+);`,
+ },
+ {
+ label: 'Express Middleware',
+ code: `import express from 'express';
+import cors from 'cors';
+import helmet from 'helmet';
+
+const app = express();
+app.use(cors());
+app.use(helmet());
+app.use(express.json());`,
+ },
+ {
+ label: 'Prisma + Next.js',
+ code: `import { PrismaClient } from '@prisma/client';
+import { NextResponse } from 'next/server';
+
+const prisma = new PrismaClient();
+
+export async function GET() {
+ const users = await prisma.user.findMany({
+ include: { posts: true },
+ });
+ return NextResponse.json(users);
+}`,
+ },
+];
+
+export function CodeInput({ onSubmit, injectedCode }: CodeInputProps) {
+ const [code, setCode] = useState(injectedCode ?? '');
+
+ const handleSubmit = () => {
+ if (code.trim().length > 10) {
+ onSubmit(code.trim());
+ }
+ };
+
+ const handlePaste = async () => {
+ try {
+ const text = await navigator.clipboard.readText();
+ setCode(text);
+ } catch {
+ // Clipboard not available
+ }
+ };
+
+ return (
+
+
+
+ Paste Unfamiliar Code
+
+
+ We'll find real-world examples from GitHub repos and Stack
+ Overflow posts that use the same libraries and APIs.
+
+
+
+
+
+
+
+
+ {code.length} characters
+ {code.trim().length > 0 && code.trim().length <= 10 && (
+
+ Paste at least 10 characters
+
+ )}
+
+
+
+
+
+
+ Or try an example:
+
+
+ {EXAMPLES.map((ex) => (
+
+ ))}
+
+
+
+ );
+}
diff --git a/code-reference-finder/src/components/Dashboard.tsx b/code-reference-finder/src/components/Dashboard.tsx
new file mode 100644
index 0000000..22f98e7
--- /dev/null
+++ b/code-reference-finder/src/components/Dashboard.tsx
@@ -0,0 +1,83 @@
+'use client';
+
+import { useState } from 'react';
+import { PipelineProgress } from './PipelineProgress';
+import { AnalysisSummary } from './AnalysisSummary';
+import { ReferenceGrid } from './ReferenceGrid';
+import { LiveBrowserPreview } from './LiveBrowserPreview';
+import { StopCircle } from 'lucide-react';
+import type { AppState } from '@/lib/types';
+
+interface DashboardProps {
+ state: AppState;
+ onCancel: () => void;
+}
+
+export function Dashboard({ state, onCancel }: DashboardProps) {
+ const [preview, setPreview] = useState<{
+ url: string;
+ title: string;
+ } | null>(null);
+
+ const isRunning =
+ state.phase === 'analyzing' ||
+ state.phase === 'searching' ||
+ state.phase === 'extracting';
+
+ const completedCount = Object.values(state.agents).filter(
+ (a) => a.status === 'complete'
+ ).length;
+ const totalCount = Object.values(state.agents).length;
+
+ return (
+
+ {/* Progress bar */}
+
+
+ {/* Analysis summary */}
+ {state.analysis &&
}
+
+ {/* Status bar */}
+
+
+ {state.searchResults.length > 0 && (
+
+ {state.searchResults.length} sources found
+
+ )}
+ {totalCount > 0 && (
+
+ {completedCount}/{totalCount} agents done
+
+ )}
+
+ {isRunning && (
+
+ )}
+
+
+ {/* Reference grid */}
+
+ setPreview({ url, title })}
+ />
+
+
+ {/* Live browser preview overlay */}
+ {preview && (
+
setPreview(null)}
+ />
+ )}
+
+ );
+}
diff --git a/code-reference-finder/src/components/Header.tsx b/code-reference-finder/src/components/Header.tsx
new file mode 100644
index 0000000..e01ddb0
--- /dev/null
+++ b/code-reference-finder/src/components/Header.tsx
@@ -0,0 +1,29 @@
+'use client';
+
+import { Code2, RotateCcw } from 'lucide-react';
+import { APP_NAME } from '@/lib/constants';
+
+interface HeaderProps {
+ showReset?: boolean;
+ onReset?: () => void;
+}
+
+export function Header({ showReset, onReset }: HeaderProps) {
+ return (
+
+
+
+
{APP_NAME}
+
+ {showReset && onReset && (
+
+ )}
+
+ );
+}
diff --git a/code-reference-finder/src/components/LiveBrowserPreview.tsx b/code-reference-finder/src/components/LiveBrowserPreview.tsx
new file mode 100644
index 0000000..13614c6
--- /dev/null
+++ b/code-reference-finder/src/components/LiveBrowserPreview.tsx
@@ -0,0 +1,131 @@
+'use client';
+
+import { useState, useEffect } from 'react';
+import { motion } from 'framer-motion';
+import { Monitor, X, Maximize2, Minimize2 } from 'lucide-react';
+
+interface LiveBrowserPreviewProps {
+ streamingUrl: string;
+ title: string;
+ onClose: () => void;
+}
+
+export function LiveBrowserPreview({
+ streamingUrl,
+ title,
+ onClose,
+}: LiveBrowserPreviewProps) {
+ const [isExpanded, setIsExpanded] = useState(false);
+ const [isLoading, setIsLoading] = useState(true);
+
+ useEffect(() => {
+ setIsLoading(true);
+ }, [streamingUrl]);
+
+ return (
+
+ {/* Header */}
+
+
+
+
+ Live: {title}
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Browser iframe */}
+
+ {isLoading && (
+
+
+
+
+ Connecting to browser...
+
+
+
+ )}
+
+
+ );
+}
+
+// Mini preview for embedding in agent cards
+interface MiniPreviewProps {
+ streamingUrl: string;
+ onClick: () => void;
+}
+
+export function MiniPreview({ streamingUrl, onClick }: MiniPreviewProps) {
+ return (
+
+
+
+
+
+ Live Preview
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/code-reference-finder/src/components/PipelineProgress.tsx b/code-reference-finder/src/components/PipelineProgress.tsx
new file mode 100644
index 0000000..1dce1d9
--- /dev/null
+++ b/code-reference-finder/src/components/PipelineProgress.tsx
@@ -0,0 +1,72 @@
+'use client';
+
+import { Check, Loader2 } from 'lucide-react';
+import type { AppPhase } from '@/lib/types';
+
+const STEPS: { phase: AppPhase; label: string }[] = [
+ { phase: 'analyzing', label: 'Analyzing Code' },
+ { phase: 'searching', label: 'Searching' },
+ { phase: 'extracting', label: 'Extracting' },
+];
+
+const PHASE_ORDER: AppPhase[] = ['input', 'analyzing', 'searching', 'extracting', 'complete'];
+
+interface PipelineProgressProps {
+ currentPhase: AppPhase;
+}
+
+export function PipelineProgress({ currentPhase }: PipelineProgressProps) {
+ const currentIndex = PHASE_ORDER.indexOf(currentPhase);
+
+ return (
+
+ {STEPS.map((step, i) => {
+ const stepIndex = PHASE_ORDER.indexOf(step.phase);
+ const isActive = step.phase === currentPhase;
+ const isDone = currentIndex > stepIndex;
+
+ return (
+
+ {i > 0 && (
+
+ )}
+
+
+ {isDone ? (
+
+ ) : isActive ? (
+
+ ) : (
+ {i + 1}
+ )}
+
+
+ {step.label}
+
+
+
+ );
+ })}
+
+ );
+}
diff --git a/code-reference-finder/src/components/ReferenceCard.tsx b/code-reference-finder/src/components/ReferenceCard.tsx
new file mode 100644
index 0000000..6877323
--- /dev/null
+++ b/code-reference-finder/src/components/ReferenceCard.tsx
@@ -0,0 +1,131 @@
+'use client';
+
+import { useState } from 'react';
+import { motion } from 'framer-motion';
+import {
+ Github,
+ MessageCircle,
+ Star,
+ ArrowUpRight,
+ ChevronDown,
+ ChevronUp,
+ ThumbsUp,
+ Tag,
+} from 'lucide-react';
+import type { ReferenceData } from '@/lib/types';
+
+interface ReferenceCardProps {
+ data: ReferenceData;
+}
+
+export function ReferenceCard({ data }: ReferenceCardProps) {
+ const [showSnippets, setShowSnippets] = useState(false);
+ const isGitHub = data.platform === 'github';
+
+ return (
+
+ {/* Header */}
+
+
+ {/* Metadata */}
+
+ {isGitHub && data.stars != null && (
+
+
+ {data.stars.toLocaleString()}
+
+ )}
+ {isGitHub && data.repoLanguage && (
+
{data.repoLanguage}
+ )}
+ {!isGitHub && data.votes != null && (
+
+
+ {data.votes}
+
+ )}
+ {data.tags && data.tags.length > 0 && (
+
+
+ {data.tags.slice(0, 3).map((tag) => (
+
+ {tag}
+
+ ))}
+
+ )}
+
+
+ {/* Alignment explanation */}
+
+ {data.alignmentExplanation}
+
+
+ {/* Description or excerpt */}
+ {isGitHub && data.repoDescription && (
+
+ {data.repoDescription}
+
+ )}
+
+ {/* Code snippets toggle */}
+ {data.codeSnippets && data.codeSnippets.length > 0 && (
+ <>
+
+
+ {showSnippets && (
+
+ {data.codeSnippets.map((snippet, i) => (
+
+ {snippet.context && (
+
+ {snippet.context}
+
+ )}
+
+ {snippet.code}
+
+
+ ))}
+
+ )}
+ >
+ )}
+
+ );
+}
diff --git a/code-reference-finder/src/components/ReferenceGrid.tsx b/code-reference-finder/src/components/ReferenceGrid.tsx
new file mode 100644
index 0000000..54410f5
--- /dev/null
+++ b/code-reference-finder/src/components/ReferenceGrid.tsx
@@ -0,0 +1,53 @@
+'use client';
+
+import { AnimatePresence } from 'framer-motion';
+import { AgentCard } from './AgentCard';
+import { ReferenceCard } from './ReferenceCard';
+import type { ReferenceAgentState } from '@/lib/types';
+
+interface ReferenceGridProps {
+ agents: Record;
+ onPreviewClick: (streamingUrl: string, title: string) => void;
+}
+
+export function ReferenceGrid({ agents, onPreviewClick }: ReferenceGridProps) {
+ const agentList = Object.values(agents);
+
+ if (agentList.length === 0) {
+ return (
+
+ Waiting for search results...
+
+ );
+ }
+
+ // Sort: completed first (by relevance score desc), then in-progress, then errors
+ const sorted = [...agentList].sort((a, b) => {
+ if (a.status === 'complete' && b.status !== 'complete') return -1;
+ if (a.status !== 'complete' && b.status === 'complete') return 1;
+ if (a.status === 'error' && b.status !== 'error') return 1;
+ if (a.status !== 'error' && b.status === 'error') return -1;
+ if (a.result && b.result) {
+ return b.result.relevanceScore - a.result.relevanceScore;
+ }
+ return 0;
+ });
+
+ return (
+
+
+ {sorted.map((agent) =>
+ agent.status === 'complete' && agent.result ? (
+
+ ) : (
+
+ )
+ )}
+
+
+ );
+}
diff --git a/code-reference-finder/src/context/AppContext.tsx b/code-reference-finder/src/context/AppContext.tsx
new file mode 100644
index 0000000..bd2d3d2
--- /dev/null
+++ b/code-reference-finder/src/context/AppContext.tsx
@@ -0,0 +1,177 @@
+'use client';
+
+import {
+ createContext,
+ useContext,
+ useReducer,
+ type ReactNode,
+ type Dispatch,
+} from 'react';
+import type { AppState, AppAction, ReferenceAgentState } from '@/lib/types';
+
+const initialState: AppState = {
+ phase: 'input',
+ userCode: null,
+ analysis: null,
+ searchQueries: [],
+ searchResults: [],
+ agents: {},
+ startedAt: null,
+ completedAt: null,
+};
+
+function allAgentsDone(agents: Record): boolean {
+ const entries = Object.values(agents);
+ if (entries.length === 0) return false;
+ return entries.every((a) => a.status === 'complete' || a.status === 'error');
+}
+
+function appReducer(state: AppState, action: AppAction): AppState {
+ switch (action.type) {
+ case 'START_ANALYSIS':
+ return {
+ ...initialState,
+ phase: 'analyzing',
+ userCode: action.payload.code,
+ startedAt: Date.now(),
+ };
+
+ case 'ANALYSIS_COMPLETE':
+ return {
+ ...state,
+ phase: 'searching',
+ analysis: action.payload.analysis,
+ searchQueries: action.payload.queries,
+ };
+
+ case 'SEARCH_COMPLETE':
+ return {
+ ...state,
+ phase: 'extracting',
+ searchResults: action.payload.results,
+ };
+
+ case 'AGENT_CONNECTING': {
+ const agent: ReferenceAgentState = {
+ id: action.payload.id,
+ url: action.payload.url,
+ platform: action.payload.platform,
+ status: 'connecting',
+ currentStep: 'Connecting to agent...',
+ steps: [],
+ startedAt: Date.now(),
+ };
+ return {
+ ...state,
+ agents: { ...state.agents, [action.payload.id]: agent },
+ };
+ }
+
+ case 'AGENT_STEP': {
+ const existing = state.agents[action.payload.id];
+ if (!existing) return state;
+ return {
+ ...state,
+ agents: {
+ ...state.agents,
+ [action.payload.id]: {
+ ...existing,
+ status: existing.platform === 'stackoverflow' ? 'reasoning' : 'navigating',
+ currentStep: action.payload.step,
+ steps: [
+ ...existing.steps,
+ { message: action.payload.step, timestamp: Date.now() },
+ ],
+ },
+ },
+ };
+ }
+
+ case 'AGENT_STREAMING_URL': {
+ const existing = state.agents[action.payload.id];
+ if (!existing) return state;
+ return {
+ ...state,
+ agents: {
+ ...state.agents,
+ [action.payload.id]: {
+ ...existing,
+ streamingUrl: action.payload.streamingUrl,
+ },
+ },
+ };
+ }
+
+ case 'AGENT_COMPLETE': {
+ const existing = state.agents[action.payload.id];
+ if (!existing) return state;
+ const updatedAgents = {
+ ...state.agents,
+ [action.payload.id]: {
+ ...existing,
+ status: 'complete' as const,
+ currentStep: 'Done',
+ result: action.payload.result,
+ completedAt: Date.now(),
+ },
+ };
+ return {
+ ...state,
+ agents: updatedAgents,
+ phase: allAgentsDone(updatedAgents) ? 'complete' : state.phase,
+ completedAt: allAgentsDone(updatedAgents) ? Date.now() : state.completedAt,
+ };
+ }
+
+ case 'AGENT_ERROR': {
+ const existing = state.agents[action.payload.id];
+ if (!existing) return state;
+ const updatedAgents = {
+ ...state.agents,
+ [action.payload.id]: {
+ ...existing,
+ status: 'error' as const,
+ currentStep: 'Failed',
+ error: action.payload.error,
+ completedAt: Date.now(),
+ },
+ };
+ return {
+ ...state,
+ agents: updatedAgents,
+ phase: allAgentsDone(updatedAgents) ? 'complete' : state.phase,
+ completedAt: allAgentsDone(updatedAgents) ? Date.now() : state.completedAt,
+ };
+ }
+
+ case 'RESET':
+ return initialState;
+
+ default:
+ return state;
+ }
+}
+
+interface AppContextValue {
+ state: AppState;
+ dispatch: Dispatch;
+}
+
+const AppContext = createContext(null);
+
+export function AppProvider({ children }: { children: ReactNode }) {
+ const [state, dispatch] = useReducer(appReducer, initialState);
+ return (
+
+ {children}
+
+ );
+}
+
+export function useAppContext(): AppContextValue {
+ const ctx = useContext(AppContext);
+ if (!ctx) {
+ throw new Error('useAppContext must be used within AppProvider');
+ }
+ return ctx;
+}
diff --git a/code-reference-finder/src/hooks/useCodeAnalysis.ts b/code-reference-finder/src/hooks/useCodeAnalysis.ts
new file mode 100644
index 0000000..108f2c9
--- /dev/null
+++ b/code-reference-finder/src/hooks/useCodeAnalysis.ts
@@ -0,0 +1,159 @@
+'use client';
+
+import { useCallback, useRef } from 'react';
+import { useAppContext } from '@/context/AppContext';
+import type {
+ OrchestratorEvent,
+ CodeAnalysis,
+ SearchQuery,
+ SearchResult,
+ ReferenceData,
+ SourcePlatform,
+} from '@/lib/types';
+
+export function useCodeAnalysis() {
+ const { state, dispatch } = useAppContext();
+ const abortRef = useRef(null);
+
+ const analyze = useCallback(
+ async (code: string) => {
+ // Cancel any in-flight request
+ abortRef.current?.abort();
+ const controller = new AbortController();
+ abortRef.current = controller;
+
+ dispatch({ type: 'START_ANALYSIS', payload: { code } });
+
+ try {
+ const response = await fetch('/api/analyze', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ code }),
+ signal: controller.signal,
+ });
+
+ if (!response.ok) {
+ const errorData = await response.json().catch(() => ({}));
+ throw new Error(errorData.error || `HTTP ${response.status}`);
+ }
+
+ if (!response.body) {
+ throw new Error('No response stream');
+ }
+
+ // Read SSE stream
+ const reader = response.body.getReader();
+ const decoder = new TextDecoder();
+ let buffer = '';
+
+ while (true) {
+ const { done, value } = await reader.read();
+ if (done) break;
+
+ buffer += decoder.decode(value, { stream: true });
+ const lines = buffer.split('\n');
+ buffer = lines.pop() ?? '';
+
+ for (const line of lines) {
+ if (!line.startsWith('data: ')) continue;
+
+ let event: OrchestratorEvent;
+ try {
+ event = JSON.parse(line.slice(6));
+ } catch {
+ continue;
+ }
+
+ switch (event.type) {
+ case 'analysis_complete':
+ dispatch({
+ type: 'ANALYSIS_COMPLETE',
+ payload: {
+ analysis: event.data.analysis as CodeAnalysis,
+ queries: event.data.queries as SearchQuery[],
+ },
+ });
+ break;
+
+ case 'search_complete':
+ dispatch({
+ type: 'SEARCH_COMPLETE',
+ payload: {
+ results: event.data.results as SearchResult[],
+ },
+ });
+ break;
+
+ case 'agent_connecting':
+ dispatch({
+ type: 'AGENT_CONNECTING',
+ payload: {
+ id: event.data.id as string,
+ url: event.data.url as string,
+ platform: event.data.platform as SourcePlatform,
+ },
+ });
+ break;
+
+ case 'agent_step':
+ dispatch({
+ type: 'AGENT_STEP',
+ payload: {
+ id: event.data.id as string,
+ step: event.data.step as string,
+ },
+ });
+ break;
+
+ case 'agent_streaming_url':
+ dispatch({
+ type: 'AGENT_STREAMING_URL',
+ payload: {
+ id: event.data.id as string,
+ streamingUrl: event.data.streamingUrl as string,
+ },
+ });
+ break;
+
+ case 'agent_complete':
+ dispatch({
+ type: 'AGENT_COMPLETE',
+ payload: {
+ id: event.data.id as string,
+ result: event.data.result as ReferenceData,
+ },
+ });
+ break;
+
+ case 'agent_error':
+ dispatch({
+ type: 'AGENT_ERROR',
+ payload: {
+ id: event.data.id as string,
+ error: event.data.error as string,
+ },
+ });
+ break;
+ }
+ }
+ }
+ } catch (error) {
+ if ((error as Error).name !== 'AbortError') {
+ console.error('Analysis failed:', error);
+ }
+ }
+ },
+ [dispatch]
+ );
+
+ const cancel = useCallback(() => {
+ abortRef.current?.abort();
+ }, []);
+
+ const reset = useCallback(() => {
+ cancel();
+ dispatch({ type: 'RESET' });
+ }, [cancel, dispatch]);
+
+ return { analyze, cancel, reset, state };
+}
diff --git a/code-reference-finder/src/lib/constants.ts b/code-reference-finder/src/lib/constants.ts
new file mode 100644
index 0000000..5f15b57
--- /dev/null
+++ b/code-reference-finder/src/lib/constants.ts
@@ -0,0 +1,15 @@
+export const MINO_API_URL = 'https://agent.tinyfish.ai/v1/automation/run-sse';
+export const OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/chat/completions';
+export const GITHUB_API_URL = 'https://api.github.com';
+export const STACKEXCHANGE_API_URL = 'https://api.stackexchange.com/2.3';
+
+export const OPENROUTER_MODEL = 'google/gemini-2.0-flash-001';
+export const OPENROUTER_TEMPERATURE = 0.2;
+
+export const MAX_AGENTS = 10;
+export const AGENT_TIMEOUT_MS = 360_000; // 6 minutes
+export const GITHUB_RESULTS_PER_QUERY = 8;
+export const STACKOVERFLOW_RESULTS_PER_QUERY = 10;
+
+export const APP_NAME = 'Code Reference Finder';
+export const APP_DESCRIPTION = 'Find real-world usage examples for unfamiliar code';
diff --git a/code-reference-finder/src/lib/goal-builder.ts b/code-reference-finder/src/lib/goal-builder.ts
new file mode 100644
index 0000000..15f2bf0
--- /dev/null
+++ b/code-reference-finder/src/lib/goal-builder.ts
@@ -0,0 +1,104 @@
+import type { CodeAnalysis, SearchResult } from './types';
+
+export function buildGitHubGoal(
+ url: string,
+ analysis: CodeAnalysis
+): { url: string; goal: string } {
+ const libs = analysis.libraries.join(', ');
+ const apis = analysis.apis.join(', ');
+
+ const goal = `You are analyzing a GitHub repository to determine how it relates to specific libraries and APIs.
+
+TARGET LIBRARIES: ${libs}
+TARGET APIs/SYMBOLS: ${apis}
+LANGUAGE: ${analysis.language}
+
+INSTRUCTIONS:
+1. Go to the repository page and read ONLY the README. Do NOT click into source files, folders, or other pages.
+2. From the README and repo description, extract: what the project does, any code examples shown, and how it relates to the target libraries/APIs.
+3. Score relevance 0-100 based on: Does the README mention/demonstrate the target libraries? Are there code examples in the README?
+
+Return a JSON object with these exact keys:
+{
+ "title": "repository full name (owner/repo)",
+ "sourceUrl": "the repository URL",
+ "platform": "github",
+ "relevanceScore": 0-100,
+ "alignmentExplanation": "2-3 sentences explaining how this repo relates to the target libraries/APIs",
+ "repoName": "owner/repo",
+ "repoDescription": "repo description text",
+ "stars": number or null,
+ "repoLanguage": "primary language",
+ "readmeExcerpt": "first 300 chars of README",
+ "codeSnippets": [
+ {
+ "code": "relevant code excerpt from the README (max 500 chars)",
+ "language": "language of the snippet",
+ "context": "README"
+ }
+ ]
+}
+
+IMPORTANT: Only read the README. Do not navigate into any source files or subdirectories. If the README has no relevant content, set relevanceScore to 0.`;
+
+ return { url, goal };
+}
+
+export function buildSOReasoningGoal(
+ searchResult: SearchResult,
+ analysis: CodeAnalysis
+): { url: string; goal: string } {
+ const libs = analysis.libraries.join(', ');
+ const apis = analysis.apis.join(', ');
+ const apiData = searchResult.apiData;
+
+ const goal = `You are a reasoning agent analyzing a Stack Overflow post to determine its relevance to specific libraries and APIs.
+
+You do NOT need to navigate anywhere. All the information you need is provided below.
+
+STACK OVERFLOW POST DATA:
+- Title: ${searchResult.title}
+- URL: ${searchResult.url}
+- Score: ${searchResult.score ?? 'unknown'}
+- Answer count: ${searchResult.answerCount ?? 'unknown'}
+- Answered: ${searchResult.isAnswered ?? 'unknown'}
+- Tags: ${searchResult.tags?.join(', ') ?? 'none'}
+- Excerpt: ${searchResult.snippet || apiData?.body_excerpt || 'No excerpt available'}
+
+TARGET LIBRARIES: ${libs}
+TARGET APIs/SYMBOLS: ${apis}
+LANGUAGE: ${analysis.language}
+
+TASK:
+Analyze the post data above and determine how relevant it is to a developer trying to understand the target libraries and APIs.
+
+Score the relevance from 0-100 based on:
+- Do the tags match the target libraries?
+- Does the title/excerpt discuss the target APIs?
+- Would this post help someone understand how to use these libraries?
+- Is the post well-received (high score, accepted answer)?
+
+Return a JSON object with these exact keys:
+{
+ "title": "${searchResult.title}",
+ "sourceUrl": "${searchResult.url}",
+ "platform": "stackoverflow",
+ "relevanceScore": 0-100,
+ "alignmentExplanation": "2-3 sentences explaining how this SO post relates to the target libraries/APIs",
+ "questionTitle": "${searchResult.title}",
+ "votes": ${searchResult.score ?? 0},
+ "tags": ${JSON.stringify(searchResult.tags ?? [])},
+ "isAccepted": ${searchResult.isAnswered ?? false},
+ "codeSnippets": [
+ {
+ "code": "any code found in the excerpt",
+ "language": "${analysis.language}",
+ "context": "Stack Overflow question/answer excerpt"
+ }
+ ]
+}
+
+If the excerpt contains no code, return an empty codeSnippets array.`;
+
+ return { url: 'https://example.com', goal };
+}
diff --git a/code-reference-finder/src/lib/mino-client.ts b/code-reference-finder/src/lib/mino-client.ts
new file mode 100644
index 0000000..2f2b8b1
--- /dev/null
+++ b/code-reference-finder/src/lib/mino-client.ts
@@ -0,0 +1,100 @@
+import { MINO_API_URL } from './constants';
+import type { MinoRequestConfig, MinoCallbacks, MinoSSEEvent } from './types';
+
+function parseSSELine(line: string): MinoSSEEvent | null {
+ if (!line.startsWith('data: ')) return null;
+ try {
+ return JSON.parse(line.slice(6)) as MinoSSEEvent;
+ } catch {
+ return null;
+ }
+}
+
+/**
+ * Start a Mino agent and handle SSE stream (server-side).
+ * Returns an AbortController for cancellation.
+ */
+export function startMinoAgent(
+ config: MinoRequestConfig,
+ callbacks: MinoCallbacks
+): AbortController {
+ const controller = new AbortController();
+ const apiKey = process.env.TINYFISH_API_KEY;
+
+ if (!apiKey || apiKey.includes('placeholder')) {
+ callbacks.onError('TINYFISH_API_KEY is not configured');
+ return controller;
+ }
+
+ fetch(MINO_API_URL, {
+ method: 'POST',
+ headers: {
+ 'X-API-Key': apiKey,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ url: config.url,
+ goal: config.goal,
+ }),
+ signal: controller.signal,
+ })
+ .then(async (response) => {
+ if (!response.ok) {
+ throw new Error(`Mino HTTP error: ${response.status}`);
+ }
+ if (!response.body) {
+ throw new Error('Mino response body is null');
+ }
+
+ const reader = response.body.getReader();
+ const decoder = new TextDecoder();
+ let buffer = '';
+ let streamingUrlCaptured = false;
+
+ while (true) {
+ const { done, value } = await reader.read();
+ if (done) break;
+
+ buffer += decoder.decode(value, { stream: true });
+ const lines = buffer.split('\n');
+ buffer = lines.pop() ?? '';
+
+ for (const line of lines) {
+ const event = parseSSELine(line);
+ if (!event) continue;
+
+ // Capture streaming URL (comes early, only once)
+ if (event.streamingUrl && !streamingUrlCaptured) {
+ streamingUrlCaptured = true;
+ callbacks.onStreamingUrl(event.streamingUrl);
+ }
+
+ // Progress steps
+ if (event.type === 'STEP' || event.purpose || event.action) {
+ callbacks.onStep(event);
+ }
+
+ // Final result
+ if (event.type === 'COMPLETE' || event.status === 'COMPLETED') {
+ if (event.resultJson) {
+ callbacks.onComplete(event.resultJson);
+ }
+ return;
+ }
+
+ // Error
+ if (event.type === 'ERROR' || event.status === 'FAILED') {
+ callbacks.onError(event.message || 'Agent automation failed');
+ return;
+ }
+ }
+ }
+ })
+ .catch((error) => {
+ if ((error as Error).name !== 'AbortError') {
+ callbacks.onError((error as Error).message);
+ }
+ });
+
+ return controller;
+}
diff --git a/code-reference-finder/src/lib/openrouter.ts b/code-reference-finder/src/lib/openrouter.ts
new file mode 100644
index 0000000..90b2bc9
--- /dev/null
+++ b/code-reference-finder/src/lib/openrouter.ts
@@ -0,0 +1,131 @@
+import { OPENROUTER_API_URL, OPENROUTER_MODEL, OPENROUTER_TEMPERATURE } from './constants';
+import type { CodeAnalysis, SearchQuery } from './types';
+
+function extractJSON(text: string): unknown {
+ try {
+ return JSON.parse(text);
+ } catch {
+ // Fallback: extract JSON object from markdown code blocks or surrounding text
+ const match = text.match(/\{[\s\S]*\}/);
+ if (match) {
+ return JSON.parse(match[0]);
+ }
+ throw new Error('Could not parse JSON from OpenRouter response');
+ }
+}
+
+async function callOpenRouter(systemPrompt: string, userPrompt: string): Promise {
+ const apiKey = process.env.OPENROUTER_API_KEY;
+ if (!apiKey || apiKey.includes('placeholder')) {
+ throw new Error('OPENROUTER_API_KEY is not configured');
+ }
+
+ const response = await fetch(OPENROUTER_API_URL, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${apiKey}`,
+ },
+ body: JSON.stringify({
+ model: OPENROUTER_MODEL,
+ temperature: OPENROUTER_TEMPERATURE,
+ messages: [
+ { role: 'system', content: systemPrompt },
+ { role: 'user', content: userPrompt },
+ ],
+ }),
+ });
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ throw new Error(`OpenRouter API error ${response.status}: ${errorText}`);
+ }
+
+ const data = await response.json();
+ return data.choices?.[0]?.message?.content ?? '';
+}
+
+export async function analyzeCode(code: string): Promise {
+ const systemPrompt = `You are a code analysis assistant.
+Analyze the following code snippet and return a structured analysis.
+
+Identify:
+1. The programming language
+2. All external libraries, packages, and frameworks imported or used
+3. All APIs, hooks, classes, and notable symbols invoked
+4. Real-world usage patterns present (e.g. data fetching, state management, authentication, middleware chaining)
+
+Do NOT return any URLs. Do NOT search the web. Only analyze the code provided.
+
+Return ONLY a JSON object with this exact shape (no markdown, no explanation):
+{
+ "language": "...",
+ "libraries": ["library1", "library2"],
+ "apis": ["api1", "api2"],
+ "patterns": ["pattern1", "pattern2"]
+}`;
+
+ const content = await callOpenRouter(systemPrompt, code);
+ const parsed = extractJSON(content) as CodeAnalysis;
+
+ return {
+ language: parsed.language || 'unknown',
+ libraries: Array.isArray(parsed.libraries) ? parsed.libraries : [],
+ apis: Array.isArray(parsed.apis) ? parsed.apis : [],
+ patterns: Array.isArray(parsed.patterns) ? parsed.patterns : [],
+ };
+}
+
+export async function generateSearchQueries(analysis: CodeAnalysis): Promise {
+ const systemPrompt = `You are a search query strategist for developer tools.
+Given the structured analysis of a code snippet, generate search queries that will surface high-quality real-world usage examples from GitHub and Stack Overflow.
+
+Requirements:
+- Generate exactly 10 search queries: 5 with target "github" and 5 with target "stackoverflow"
+- Keep queries SHORT (2-4 words max). Shorter queries return more results.
+- For GitHub: use library/framework names only, e.g. "tanstack react-query", "express middleware typescript"
+- For Stack Overflow: use concise problem keywords, e.g. "useQuery refetch interval", "prisma findMany include"
+- Do NOT write full sentences or long phrases as queries
+- For each query, indicate the intended target: "github" or "stackoverflow"
+- Provide ranking heuristics: what signals indicate a high-quality result
+
+Do NOT return any URLs. Do NOT invent links. Only return queries and heuristics.
+
+Return ONLY a JSON object with this exact shape (no markdown, no explanation):
+{
+ "queries": [
+ {
+ "query": "search terms here",
+ "target": "github",
+ "heuristic": "what makes a good result for this query"
+ }
+ ]
+}`;
+
+ const userPrompt = `Language: ${analysis.language}
+Libraries: ${analysis.libraries.join(', ')}
+APIs: ${analysis.apis.join(', ')}
+Patterns: ${analysis.patterns.join(', ')}`;
+
+ const content = await callOpenRouter(systemPrompt, userPrompt);
+ const parsed = extractJSON(content) as { queries: SearchQuery[] };
+
+ if (!Array.isArray(parsed.queries)) {
+ throw new Error('OpenRouter did not return a queries array');
+ }
+
+ const all = parsed.queries
+ .filter((q) => q.query && q.target && q.heuristic)
+ .map((q) => {
+ // Normalize target: LLMs may return "GitHub", "StackOverflow", "stack_overflow", etc.
+ const t = q.target.toLowerCase().replace(/[\s_-]/g, '');
+ const target = t.includes('stack') ? 'stackoverflow' : 'github';
+ return { ...q, target } as SearchQuery;
+ });
+
+ // Guarantee exactly 5 of each platform
+ const ghQueries = all.filter((q) => q.target === 'github').slice(0, 5);
+ const soQueries = all.filter((q) => q.target === 'stackoverflow').slice(0, 5);
+
+ return [...ghQueries, ...soQueries];
+}
diff --git a/code-reference-finder/src/lib/orchestrator.ts b/code-reference-finder/src/lib/orchestrator.ts
new file mode 100644
index 0000000..30cc2be
--- /dev/null
+++ b/code-reference-finder/src/lib/orchestrator.ts
@@ -0,0 +1,171 @@
+import { analyzeCode, generateSearchQueries } from './openrouter';
+import { executeSearches } from './search';
+import { startMinoAgent } from './mino-client';
+import { buildGitHubGoal, buildSOReasoningGoal } from './goal-builder';
+import { AGENT_TIMEOUT_MS } from './constants';
+import type { CodeAnalysis, OrchestratorEvent, SearchResult, ReferenceData } from './types';
+
+const encoder = new TextEncoder();
+
+function emitEvent(
+ writer: WritableStreamDefaultWriter,
+ event: OrchestratorEvent
+) {
+ const payload = `data: ${JSON.stringify(event)}\n\n`;
+ writer.write(encoder.encode(payload)).catch(() => {
+ // Stream may be closed by client — ignore
+ });
+}
+
+function makeAgentId(index: number, platform: string): string {
+ return `agent-${platform}-${index}-${Date.now()}`;
+}
+
+function launchAgent(
+ agentId: string,
+ searchResult: SearchResult,
+ analysis: CodeAnalysis,
+ writer: WritableStreamDefaultWriter
+): Promise {
+ return new Promise((resolve) => {
+ // Build the appropriate goal
+ const config =
+ searchResult.platform === 'github'
+ ? buildGitHubGoal(searchResult.url, analysis)
+ : buildSOReasoningGoal(searchResult, analysis);
+
+ emitEvent(writer, {
+ type: 'agent_connecting',
+ data: {
+ id: agentId,
+ url: searchResult.url,
+ platform: searchResult.platform,
+ title: searchResult.title,
+ },
+ });
+
+ let controller: AbortController;
+
+ // Timeout
+ const timeout = setTimeout(() => {
+ controller?.abort();
+ emitEvent(writer, {
+ type: 'agent_error',
+ data: { id: agentId, error: 'Agent timed out after 6 minutes' },
+ });
+ resolve();
+ }, AGENT_TIMEOUT_MS);
+
+ controller = startMinoAgent(config, {
+ onStep(event) {
+ const message =
+ event.message || event.purpose || event.action || 'Working...';
+ emitEvent(writer, {
+ type: 'agent_step',
+ data: { id: agentId, step: message },
+ });
+ },
+
+ onStreamingUrl(url) {
+ emitEvent(writer, {
+ type: 'agent_streaming_url',
+ data: { id: agentId, streamingUrl: url },
+ });
+ },
+
+ onComplete(resultJson) {
+ clearTimeout(timeout);
+ const result = resultJson as ReferenceData;
+ // Ensure required fields have defaults
+ const normalized: ReferenceData = {
+ sourceUrl: result.sourceUrl || searchResult.url,
+ platform: searchResult.platform,
+ title: result.title || searchResult.title,
+ relevanceScore: result.relevanceScore ?? 50,
+ alignmentExplanation: result.alignmentExplanation || '',
+ codeSnippets: Array.isArray(result.codeSnippets) ? result.codeSnippets : [],
+ repoName: result.repoName,
+ repoDescription: result.repoDescription,
+ stars: result.stars,
+ repoLanguage: result.repoLanguage,
+ readmeExcerpt: result.readmeExcerpt,
+ questionTitle: result.questionTitle,
+ votes: result.votes,
+ answerSnippets: result.answerSnippets,
+ tags: result.tags,
+ isAccepted: result.isAccepted,
+ };
+ emitEvent(writer, {
+ type: 'agent_complete',
+ data: { id: agentId, result: normalized },
+ });
+ resolve();
+ },
+
+ onError(error) {
+ clearTimeout(timeout);
+ emitEvent(writer, {
+ type: 'agent_error',
+ data: { id: agentId, error },
+ });
+ resolve();
+ },
+ });
+ });
+}
+
+export async function runPipeline(
+ code: string,
+ writer: WritableStreamDefaultWriter
+): Promise {
+ try {
+ // Stage 1: Analyze code + generate queries
+ const analysis = await analyzeCode(code);
+ const queries = await generateSearchQueries(analysis);
+
+ emitEvent(writer, {
+ type: 'analysis_complete',
+ data: {
+ analysis,
+ queries,
+ },
+ });
+
+ // Stage 2: Execute indexed searches
+ const searchResults = await executeSearches(queries);
+
+ emitEvent(writer, {
+ type: 'search_complete',
+ data: {
+ results: searchResults,
+ },
+ });
+
+ if (searchResults.length === 0) {
+ emitEvent(writer, {
+ type: 'pipeline_complete',
+ data: { message: 'No search results found' },
+ });
+ return;
+ }
+
+ // Stage 3: Launch parallel Mino agents
+ const agentPromises = searchResults.map((result, index) => {
+ const agentId = makeAgentId(index, result.platform);
+ return launchAgent(agentId, result, analysis, writer);
+ });
+
+ await Promise.allSettled(agentPromises);
+
+ // Stage 4: Pipeline complete
+ emitEvent(writer, {
+ type: 'pipeline_complete',
+ data: { message: 'All agents finished' },
+ });
+ } catch (error) {
+ emitEvent(writer, {
+ type: 'pipeline_error',
+ data: { error: (error as Error).message },
+ });
+ }
+}
diff --git a/code-reference-finder/src/lib/search.ts b/code-reference-finder/src/lib/search.ts
new file mode 100644
index 0000000..999b860
--- /dev/null
+++ b/code-reference-finder/src/lib/search.ts
@@ -0,0 +1,175 @@
+import {
+ GITHUB_API_URL,
+ STACKEXCHANGE_API_URL,
+ GITHUB_RESULTS_PER_QUERY,
+ STACKOVERFLOW_RESULTS_PER_QUERY,
+ MAX_AGENTS,
+} from './constants';
+import type { SearchQuery, SearchResult, StackExchangeItem } from './types';
+
+interface GitHubRepoItem {
+ html_url: string;
+ full_name: string;
+ description: string | null;
+ stargazers_count: number;
+ language: string | null;
+}
+
+function delay(ms: number): Promise {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+}
+
+async function fetchWithRetry(
+ url: string,
+ options: RequestInit,
+ retries = 3
+): Promise {
+ for (let attempt = 0; attempt < retries; attempt++) {
+ const response = await fetch(url, options);
+
+ if (response.status === 429) {
+ const retryAfter = response.headers.get('Retry-After');
+ const waitMs = retryAfter
+ ? parseInt(retryAfter, 10) * 1000
+ : (attempt + 1) * 5000; // 5s, 10s, 15s
+ console.warn(`GitHub 429 rate-limited. Retrying in ${waitMs}ms (attempt ${attempt + 1}/${retries})`);
+ await delay(waitMs);
+ continue;
+ }
+
+ return response;
+ }
+
+ return fetch(url, options);
+}
+
+async function searchGitHub(query: string): Promise {
+ const params = new URLSearchParams({
+ q: query,
+ sort: 'stars',
+ per_page: String(GITHUB_RESULTS_PER_QUERY),
+ });
+
+ const headers: Record = {
+ Accept: 'application/vnd.github.v3+json',
+ };
+
+ const token = process.env.GITHUB_TOKEN;
+ if (token && !token.includes('placeholder')) {
+ // Use Bearer prefix — works for both classic (ghp_) and fine-grained (github_pat_) tokens
+ headers['Authorization'] = `Bearer ${token}`;
+ }
+
+ const response = await fetchWithRetry(
+ `${GITHUB_API_URL}/search/repositories?${params}`,
+ { headers }
+ );
+
+ if (!response.ok) {
+ console.error(`GitHub search failed: ${response.status}`);
+ return [];
+ }
+
+ const data = await response.json();
+ const items: GitHubRepoItem[] = data.items ?? [];
+
+ return items.map((item) => ({
+ platform: 'github' as const,
+ url: item.html_url,
+ title: item.full_name,
+ snippet: item.description ?? '',
+ stars: item.stargazers_count,
+ language: item.language ?? undefined,
+ }));
+}
+
+async function searchStackOverflow(query: string): Promise {
+ const params = new URLSearchParams({
+ order: 'desc',
+ sort: 'votes',
+ q: query,
+ site: 'stackoverflow',
+ pagesize: String(STACKOVERFLOW_RESULTS_PER_QUERY),
+ filter: '!nNPvSNdWme',
+ });
+
+ const key = process.env.STACKEXCHANGE_KEY;
+ if (key && !key.includes('placeholder')) {
+ params.set('key', key);
+ }
+
+ try {
+ const response = await fetch(`${STACKEXCHANGE_API_URL}/search/advanced?${params}`, {
+ headers: { 'User-Agent': 'CodeReferenceFinder/1.0' },
+ });
+
+ if (!response.ok) {
+ console.error(`SO search failed: ${response.status} for "${query}"`);
+ return [];
+ }
+
+ // Node.js fetch decompresses gzip automatically — just use .json()
+ const data = await response.json();
+
+ if (data.error_id) {
+ console.error(`SO API error: ${data.error_name} — ${data.error_message}`);
+ return [];
+ }
+
+ const items: StackExchangeItem[] = data.items ?? [];
+
+ return items.map((item) => ({
+ platform: 'stackoverflow' as const,
+ url: item.link,
+ title: item.title,
+ snippet: item.body_excerpt ?? '',
+ score: item.score,
+ answerCount: item.answer_count,
+ tags: item.tags,
+ isAnswered: item.is_answered,
+ apiData: item,
+ }));
+ } catch (err) {
+ console.error(`SO search error for "${query}":`, err);
+ return [];
+ }
+}
+
+function deduplicate(results: SearchResult[]): SearchResult[] {
+ const seen = new Set();
+ return results.filter((r) => {
+ if (seen.has(r.url)) return false;
+ seen.add(r.url);
+ return true;
+ });
+}
+
+export async function executeSearches(queries: SearchQuery[]): Promise {
+ const githubQueries = queries.filter((q) => q.target === 'github');
+ const soQueries = queries.filter((q) => q.target === 'stackoverflow');
+ const halfTarget = Math.ceil(MAX_AGENTS / 2); // 5
+
+ // Run SO queries in parallel (no strict rate limits)
+ const soResults = (await Promise.all(
+ soQueries.map((q) => searchStackOverflow(q.query))
+ )).flat();
+
+ // Run GitHub queries sequentially with a 2s gap to avoid 429 rate limiting
+ const githubResults: SearchResult[] = [];
+ for (let i = 0; i < githubQueries.length; i++) {
+ const results = await searchGitHub(githubQueries[i].query);
+ githubResults.push(...results);
+ if (i < githubQueries.length - 1) {
+ await delay(2000);
+ }
+ }
+
+ const dedupedGH = deduplicate(githubResults);
+ const dedupedSO = deduplicate(soResults);
+
+ // Strict 5+5 split: take up to 5 from each platform
+ const pickedGH = dedupedGH.slice(0, halfTarget);
+ const pickedSO = dedupedSO.slice(0, halfTarget);
+
+ return [...pickedGH, ...pickedSO];
+}
diff --git a/code-reference-finder/src/lib/types.ts b/code-reference-finder/src/lib/types.ts
new file mode 100644
index 0000000..9ae7809
--- /dev/null
+++ b/code-reference-finder/src/lib/types.ts
@@ -0,0 +1,172 @@
+// --- Source Platform ---
+export type SourcePlatform = 'github' | 'stackoverflow';
+
+// --- OpenRouter Analysis Output ---
+export interface CodeAnalysis {
+ language: string;
+ libraries: string[];
+ apis: string[];
+ patterns: string[];
+}
+
+// --- Search Query (generated by OpenRouter) ---
+export interface SearchQuery {
+ query: string;
+ target: 'github' | 'stackoverflow';
+ heuristic: string;
+}
+
+// --- Stack Exchange API item shape ---
+export interface StackExchangeItem {
+ question_id: number;
+ title: string;
+ tags: string[];
+ score: number;
+ answer_count: number;
+ is_answered: boolean;
+ link: string;
+ body_excerpt?: string;
+}
+
+// --- Search Result (from indexed APIs) ---
+export interface SearchResult {
+ platform: SourcePlatform;
+ url: string;
+ title: string;
+ snippet: string;
+ stars?: number;
+ language?: string;
+ score?: number;
+ answerCount?: number;
+ tags?: string[];
+ isAnswered?: boolean;
+ apiData?: StackExchangeItem;
+}
+
+// --- Code Snippet extracted by agents ---
+export interface CodeSnippet {
+ code: string;
+ language: string;
+ context: string;
+}
+
+// --- Extracted Reference Data (from Mino agents) ---
+export interface ReferenceData {
+ sourceUrl: string;
+ platform: SourcePlatform;
+ title: string;
+ relevanceScore: number;
+ alignmentExplanation: string;
+ codeSnippets: CodeSnippet[];
+ repoName?: string;
+ repoDescription?: string;
+ stars?: number;
+ repoLanguage?: string;
+ readmeExcerpt?: string;
+ questionTitle?: string;
+ votes?: number;
+ answerSnippets?: string[];
+ tags?: string[];
+ isAccepted?: boolean;
+}
+
+// --- Agent State ---
+export type AgentStatus =
+ | 'connecting'
+ | 'navigating'
+ | 'extracting'
+ | 'reasoning'
+ | 'complete'
+ | 'error';
+
+export interface AgentStep {
+ message: string;
+ timestamp: number;
+}
+
+export interface ReferenceAgentState {
+ id: string;
+ url: string;
+ platform: SourcePlatform;
+ status: AgentStatus;
+ currentStep: string;
+ steps: AgentStep[];
+ streamingUrl?: string;
+ result?: ReferenceData;
+ error?: string;
+ startedAt?: number;
+ completedAt?: number;
+}
+
+// --- App State ---
+export type AppPhase =
+ | 'input'
+ | 'analyzing'
+ | 'searching'
+ | 'extracting'
+ | 'complete';
+
+export interface AppState {
+ phase: AppPhase;
+ userCode: string | null;
+ analysis: CodeAnalysis | null;
+ searchQueries: SearchQuery[];
+ searchResults: SearchResult[];
+ agents: Record;
+ startedAt: number | null;
+ completedAt: number | null;
+}
+
+// --- Reducer Actions ---
+export type AppAction =
+ | { type: 'START_ANALYSIS'; payload: { code: string } }
+ | { type: 'ANALYSIS_COMPLETE'; payload: { analysis: CodeAnalysis; queries: SearchQuery[] } }
+ | { type: 'SEARCH_COMPLETE'; payload: { results: SearchResult[] } }
+ | { type: 'AGENT_CONNECTING'; payload: { id: string; url: string; platform: SourcePlatform } }
+ | { type: 'AGENT_STEP'; payload: { id: string; step: string } }
+ | { type: 'AGENT_STREAMING_URL'; payload: { id: string; streamingUrl: string } }
+ | { type: 'AGENT_COMPLETE'; payload: { id: string; result: ReferenceData } }
+ | { type: 'AGENT_ERROR'; payload: { id: string; error: string } }
+ | { type: 'RESET' };
+
+// --- Mino SSE types ---
+export interface MinoSSEEvent {
+ type?: string;
+ status?: string;
+ message?: string;
+ purpose?: string;
+ action?: string;
+ resultJson?: unknown;
+ streamingUrl?: string;
+ step?: number;
+ totalSteps?: number;
+}
+
+export interface MinoCallbacks {
+ onStep: (event: MinoSSEEvent) => void;
+ onStreamingUrl: (url: string) => void;
+ onComplete: (result: unknown) => void;
+ onError: (error: string) => void;
+}
+
+export interface MinoRequestConfig {
+ url: string;
+ goal: string;
+}
+
+// --- SSE Orchestration Events (API route -> client) ---
+export type OrchestratorEventType =
+ | 'analysis_complete'
+ | 'search_complete'
+ | 'agent_connecting'
+ | 'agent_step'
+ | 'agent_streaming_url'
+ | 'agent_complete'
+ | 'agent_error'
+ | 'pipeline_complete'
+ | 'pipeline_error';
+
+export interface OrchestratorEvent {
+ type: OrchestratorEventType;
+ data: Record;
+}
diff --git a/code-reference-finder/tsconfig.json b/code-reference-finder/tsconfig.json
new file mode 100644
index 0000000..cf9c65d
--- /dev/null
+++ b/code-reference-finder/tsconfig.json
@@ -0,0 +1,34 @@
+{
+ "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": "react-jsx",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts",
+ ".next/dev/types/**/*.ts",
+ "**/*.mts"
+ ],
+ "exclude": ["node_modules"]
+}