main 8fd712b8d245 cached
1181 files
4.2 MB
1.2M tokens
2815 symbols
1 requests
Download .txt
Showing preview only (4,633K chars total). Download the full file or copy to clipboard to get everything.
Repository: tinyfish-io/tinyfish-cookbook
Branch: main
Commit: 8fd712b8d245
Files: 1181
Total size: 4.2 MB

Directory structure:
gitextract_gre32cio/

├── .github/
│   ├── CODEOWNERS
│   ├── config/
│   │   └── .pre-commit-config-template.yaml
│   └── workflows/
│       ├── secrets-scanner.yml
│       └── vuln-scanner-pr.yml
├── .semgrepignore
├── .tags.json
├── .yamllint
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── Manga-Availability-Finder/
│   ├── README.md
│   ├── docs/
│   │   └── MINO_API_INTEGRATION.md
│   ├── package.json
│   ├── public/
│   │   └── robots.txt
│   ├── src/
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── AgentCard.tsx
│   │   │   ├── NavLink.tsx
│   │   │   ├── ResultsSummary.tsx
│   │   │   ├── SearchHero.tsx
│   │   │   └── ui/
│   │   │       ├── avatar.tsx
│   │   │       ├── button.tsx
│   │   │       └── card.tsx
│   │   ├── hooks/
│   │   │   └── useMangaSearch.ts
│   │   ├── integrations/
│   │   │   └── supabase/
│   │   │       ├── client.ts
│   │   │       └── types.ts
│   │   ├── lib/
│   │   │   └── utils.ts
│   │   └── pages/
│   │       ├── Index.tsx
│   │       └── NotFound.tsx
│   ├── supabase/
│   │   ├── config.toml
│   │   └── functions/
│   │       ├── discover-manga-sites/
│   │       │   └── index.ts
│   │       └── search-manga/
│   │           └── index.ts
│   └── tsconfig.json
├── N8N_WorkFlows/
│   ├── Competitor Scout CLI/
│   │   ├── Competitor Scout via Tinyfish.json
│   │   └── README.md
│   ├── Daily Product Hunt Tracker/
│   │   ├── Daily Product Hunt Tracker via Tinyfish.json
│   │   └── README.md
│   ├── Web Research Agent/
│   │   ├── README.md
│   │   └── Web Research Agent via Tinyfish.json
│   └── ai-competitor-analysis/
│       ├── README.md
│       └── ai-competitor-radar.json
├── README.md
├── anime-watch-hub/
│   ├── .gitignore
│   ├── README.md
│   ├── app/
│   │   ├── api/
│   │   │   ├── check-platform/
│   │   │   │   └── route.ts
│   │   │   └── discover-platforms/
│   │   │       └── route.ts
│   │   ├── globals.css
│   │   ├── layout.tsx
│   │   ├── loading.tsx
│   │   └── page.tsx
│   ├── components/
│   │   ├── anime-watch-hub.tsx
│   │   ├── platform-card.tsx
│   │   ├── results-sidebar.tsx
│   │   ├── theme-provider.tsx
│   │   └── ui/
│   │       ├── accordion.tsx
│   │       ├── alert-dialog.tsx
│   │       ├── alert.tsx
│   │       ├── aspect-ratio.tsx
│   │       ├── avatar.tsx
│   │       ├── badge.tsx
│   │       ├── breadcrumb.tsx
│   │       ├── button-group.tsx
│   │       ├── button.tsx
│   │       ├── calendar.tsx
│   │       ├── card.tsx
│   │       ├── carousel.tsx
│   │       ├── chart.tsx
│   │       ├── checkbox.tsx
│   │       ├── collapsible.tsx
│   │       ├── command.tsx
│   │       ├── context-menu.tsx
│   │       ├── dialog.tsx
│   │       ├── drawer.tsx
│   │       ├── dropdown-menu.tsx
│   │       ├── empty.tsx
│   │       ├── field.tsx
│   │       ├── form.tsx
│   │       ├── hover-card.tsx
│   │       ├── input-group.tsx
│   │       ├── input-otp.tsx
│   │       ├── input.tsx
│   │       ├── item.tsx
│   │       ├── kbd.tsx
│   │       ├── label.tsx
│   │       ├── menubar.tsx
│   │       ├── navigation-menu.tsx
│   │       ├── pagination.tsx
│   │       ├── popover.tsx
│   │       ├── progress.tsx
│   │       ├── radio-group.tsx
│   │       ├── resizable.tsx
│   │       ├── scroll-area.tsx
│   │       ├── select.tsx
│   │       ├── separator.tsx
│   │       ├── sheet.tsx
│   │       ├── sidebar.tsx
│   │       ├── skeleton.tsx
│   │       ├── slider.tsx
│   │       ├── sonner.tsx
│   │       ├── spinner.tsx
│   │       ├── switch.tsx
│   │       ├── table.tsx
│   │       ├── tabs.tsx
│   │       ├── textarea.tsx
│   │       ├── toast.tsx
│   │       ├── toaster.tsx
│   │       ├── toggle-group.tsx
│   │       ├── toggle.tsx
│   │       ├── tooltip.tsx
│   │       ├── use-mobile.tsx
│   │       └── use-toast.ts
│   ├── components.json
│   ├── docs/
│   │   └── mino-api-integration.md
│   ├── hooks/
│   │   ├── use-anime-search.ts
│   │   ├── use-mobile.ts
│   │   └── use-toast.ts
│   ├── lib/
│   │   ├── types.ts
│   │   └── utils.ts
│   ├── next.config.mjs
│   ├── package.json
│   ├── postcss.config.mjs
│   ├── styles/
│   │   └── globals.css
│   └── tsconfig.json
├── bestbet/
│   ├── .gitignore
│   ├── README.md
│   ├── app/
│   │   ├── components/
│   │   │   ├── MoneyParticle.tsx
│   │   │   └── SportsbookSelector.tsx
│   │   ├── globals.css
│   │   ├── layout.tsx
│   │   ├── page.tsx
│   │   └── webagent.ts
│   ├── eslint.config.mjs
│   ├── next.config.ts
│   ├── package.json
│   ├── postcss.config.mjs
│   └── tsconfig.json
├── code-reference-finder/
│   ├── .gitignore
│   ├── README.md
│   ├── extension/
│   │   ├── background.js
│   │   ├── manifest.json
│   │   ├── sidepanel.html
│   │   └── sidepanel.js
│   ├── next.config.ts
│   ├── package.json
│   ├── postcss.config.mjs
│   ├── src/
│   │   ├── app/
│   │   │   ├── api/
│   │   │   │   └── analyze/
│   │   │   │       └── route.ts
│   │   │   ├── globals.css
│   │   │   ├── layout.tsx
│   │   │   └── page.tsx
│   │   ├── components/
│   │   │   ├── AgentCard.tsx
│   │   │   ├── AnalysisSummary.tsx
│   │   │   ├── CodeInput.tsx
│   │   │   ├── Dashboard.tsx
│   │   │   ├── Header.tsx
│   │   │   ├── LiveBrowserPreview.tsx
│   │   │   ├── PipelineProgress.tsx
│   │   │   ├── ReferenceCard.tsx
│   │   │   └── ReferenceGrid.tsx
│   │   ├── context/
│   │   │   └── AppContext.tsx
│   │   ├── hooks/
│   │   │   └── useCodeAnalysis.ts
│   │   └── lib/
│   │       ├── constants.ts
│   │       ├── goal-builder.ts
│   │       ├── mino-client.ts
│   │       ├── openrouter.ts
│   │       ├── orchestrator.ts
│   │       ├── search.ts
│   │       └── types.ts
│   └── tsconfig.json
├── competitor-analysis/
│   ├── .gitignore
│   ├── .mcp.json
│   ├── CHANGELOG.md
│   ├── FEATURES.md
│   ├── MINO_API_FEEDBACK.md
│   ├── README.md
│   ├── app/
│   │   ├── analysis/
│   │   │   └── page.tsx
│   │   ├── api/
│   │   │   ├── analyze-pricing/
│   │   │   │   └── route.ts
│   │   │   ├── generate-urls/
│   │   │   │   └── route.ts
│   │   │   └── scrape-pricing/
│   │   │       └── route.ts
│   │   ├── company/
│   │   │   └── [id]/
│   │   │       └── page.tsx
│   │   ├── competitors/
│   │   │   └── page.tsx
│   │   ├── dashboard/
│   │   │   └── page.tsx
│   │   ├── globals.css
│   │   ├── layout.tsx
│   │   └── page.tsx
│   ├── components/
│   │   ├── competitor-input.tsx
│   │   ├── dashboard-layout.tsx
│   │   ├── settings-panel.tsx
│   │   ├── spreadsheet-view.tsx
│   │   └── ui/
│   │       ├── badge.tsx
│   │       ├── button.tsx
│   │       ├── card.tsx
│   │       ├── dialog.tsx
│   │       ├── dot-pattern.tsx
│   │       ├── input.tsx
│   │       ├── select.tsx
│   │       ├── separator.tsx
│   │       ├── sheet.tsx
│   │       ├── sidebar.tsx
│   │       ├── skeleton.tsx
│   │       ├── table.tsx
│   │       ├── tabs.tsx
│   │       ├── textarea.tsx
│   │       └── tooltip.tsx
│   ├── components.json
│   ├── eslint.config.mjs
│   ├── hooks/
│   │   └── use-mobile.ts
│   ├── lib/
│   │   ├── ai-client.ts
│   │   ├── ai-schemas.ts
│   │   ├── mino-client.ts
│   │   ├── pricing-context.tsx
│   │   └── utils.ts
│   ├── next.config.ts
│   ├── package.json
│   ├── postcss.config.mjs
│   ├── tsconfig.json
│   └── types/
│       └── index.ts
├── competitor-scout-cli/
│   ├── .gitignore
│   ├── .vscode/
│   │   └── settings.json
│   ├── FILE_ARCHITECTURE.md
│   ├── PRODUCT.md
│   ├── README.md
│   ├── app/
│   │   ├── api/
│   │   │   └── research/
│   │   │       └── route.ts
│   │   ├── globals.css
│   │   ├── layout.tsx
│   │   └── page.tsx
│   ├── cli/
│   │   └── scout.mjs
│   ├── components/
│   │   ├── cli-preview.tsx
│   │   ├── competitor-panel.tsx
│   │   ├── event-log.tsx
│   │   ├── query-input.tsx
│   │   └── report-view.tsx
│   ├── lib/
│   │   ├── env.ts
│   │   ├── openai-client.ts
│   │   ├── tinyfish.ts
│   │   ├── types.ts
│   │   └── utils.ts
│   ├── next-env.d.ts
│   ├── next.config.mjs
│   ├── package.json
│   ├── postcss.config.mjs
│   └── tsconfig.json
├── concept-discovery-system/
│   ├── .gitignore
│   ├── README.md
│   ├── eslint.config.js
│   ├── index.html
│   ├── package.json
│   ├── src/
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── ErrorBoundary.tsx
│   │   │   ├── input/
│   │   │   │   └── InputForm.tsx
│   │   │   ├── layout/
│   │   │   │   ├── Footer.tsx
│   │   │   │   └── Header.tsx
│   │   │   └── results/
│   │   │       ├── AnalysisPanel.tsx
│   │   │       ├── ConceptCard.tsx
│   │   │       ├── ConceptCardLoading.tsx
│   │   │       ├── Dashboard.tsx
│   │   │       ├── DetailPanel.tsx
│   │   │       └── ResultsGrid.tsx
│   │   ├── context/
│   │   │   └── DiscoveryContext.tsx
│   │   ├── hooks/
│   │   │   └── useConceptDiscovery.ts
│   │   ├── index.css
│   │   ├── lib/
│   │   │   ├── constants.ts
│   │   │   ├── goal-builder.ts
│   │   │   ├── openrouter-client.ts
│   │   │   ├── query-generator.ts
│   │   │   ├── search-engines.ts
│   │   │   ├── tinyfish-client.ts
│   │   │   └── utils.ts
│   │   ├── main.tsx
│   │   └── types/
│   │       └── index.ts
│   ├── tsconfig.app.json
│   ├── tsconfig.json
│   ├── tsconfig.node.json
│   ├── vercel.json
│   └── vite.config.ts
├── fast-qa/
│   ├── .gitignore
│   ├── README.md
│   ├── app/
│   │   ├── api/
│   │   │   ├── execute-tests/
│   │   │   │   └── route.ts
│   │   │   ├── generate-report/
│   │   │   │   └── route.ts
│   │   │   ├── generate-tests/
│   │   │   │   └── route.ts
│   │   │   └── parse-test/
│   │   │       └── route.ts
│   │   ├── globals.css
│   │   ├── layout.tsx
│   │   └── page.tsx
│   ├── components/
│   │   ├── qa/
│   │   │   ├── ai-test-generator.tsx
│   │   │   ├── dashboard-layout.tsx
│   │   │   ├── index.ts
│   │   │   ├── project-card.tsx
│   │   │   ├── project-dialog.tsx
│   │   │   ├── settings-panel.tsx
│   │   │   ├── test-case-detail.tsx
│   │   │   ├── test-case-editor.tsx
│   │   │   ├── test-case-list.tsx
│   │   │   ├── test-execution-grid.tsx
│   │   │   └── test-results-table.tsx
│   │   └── ui/
│   │       ├── accordion.tsx
│   │       ├── alert-dialog.tsx
│   │       ├── alert.tsx
│   │       ├── badge.tsx
│   │       ├── button.tsx
│   │       ├── card.tsx
│   │       ├── checkbox.tsx
│   │       ├── collapsible.tsx
│   │       ├── dialog.tsx
│   │       ├── dropdown-menu.tsx
│   │       ├── form.tsx
│   │       ├── input.tsx
│   │       ├── label.tsx
│   │       ├── progress.tsx
│   │       ├── select.tsx
│   │       ├── separator.tsx
│   │       ├── sheet.tsx
│   │       ├── skeleton.tsx
│   │       ├── switch.tsx
│   │       ├── table.tsx
│   │       ├── tabs.tsx
│   │       ├── textarea.tsx
│   │       └── tooltip.tsx
│   ├── components.json
│   ├── eslint.config.mjs
│   ├── lib/
│   │   ├── ai-client.ts
│   │   ├── hooks.ts
│   │   ├── mino-client.ts
│   │   ├── qa-context.tsx
│   │   └── utils.ts
│   ├── next.config.ts
│   ├── package.json
│   ├── postcss.config.mjs
│   ├── tsconfig.json
│   └── types/
│       └── index.ts
├── game-buying-guide/
│   ├── .gitignore
│   ├── README.md
│   ├── app/
│   │   ├── api/
│   │   │   ├── analyze-platform/
│   │   │   │   └── route.ts
│   │   │   ├── discover-platforms/
│   │   │   │   └── route.ts
│   │   │   └── steamdb-price-history/
│   │   │       └── route.ts
│   │   ├── globals.css
│   │   ├── layout.tsx
│   │   ├── loading.tsx
│   │   └── page.tsx
│   ├── components/
│   │   ├── agent-card.tsx
│   │   ├── agent-grid.tsx
│   │   ├── live-browser-preview.tsx
│   │   ├── results-summary.tsx
│   │   ├── search-form.tsx
│   │   ├── steamdb-price-card.tsx
│   │   ├── theme-provider.tsx
│   │   └── ui/
│   │       ├── accordion.tsx
│   │       ├── alert-dialog.tsx
│   │       ├── alert.tsx
│   │       ├── aspect-ratio.tsx
│   │       ├── avatar.tsx
│   │       ├── badge.tsx
│   │       ├── breadcrumb.tsx
│   │       ├── button-group.tsx
│   │       ├── button.tsx
│   │       ├── calendar.tsx
│   │       ├── card.tsx
│   │       ├── carousel.tsx
│   │       ├── chart.tsx
│   │       ├── checkbox.tsx
│   │       ├── collapsible.tsx
│   │       ├── command.tsx
│   │       ├── context-menu.tsx
│   │       ├── dialog.tsx
│   │       ├── drawer.tsx
│   │       ├── dropdown-menu.tsx
│   │       ├── empty.tsx
│   │       ├── field.tsx
│   │       ├── form.tsx
│   │       ├── hover-card.tsx
│   │       ├── input-group.tsx
│   │       ├── input-otp.tsx
│   │       ├── input.tsx
│   │       ├── item.tsx
│   │       ├── kbd.tsx
│   │       ├── label.tsx
│   │       ├── menubar.tsx
│   │       ├── navigation-menu.tsx
│   │       ├── pagination.tsx
│   │       ├── popover.tsx
│   │       ├── progress.tsx
│   │       ├── radio-group.tsx
│   │       ├── resizable.tsx
│   │       ├── scroll-area.tsx
│   │       ├── select.tsx
│   │       ├── separator.tsx
│   │       ├── sheet.tsx
│   │       ├── sidebar.tsx
│   │       ├── skeleton.tsx
│   │       ├── slider.tsx
│   │       ├── sonner.tsx
│   │       ├── spinner.tsx
│   │       ├── switch.tsx
│   │       ├── table.tsx
│   │       ├── tabs.tsx
│   │       ├── textarea.tsx
│   │       ├── toast.tsx
│   │       ├── toaster.tsx
│   │       ├── toggle-group.tsx
│   │       ├── toggle.tsx
│   │       ├── tooltip.tsx
│   │       ├── use-mobile.tsx
│   │       └── use-toast.ts
│   ├── components.json
│   ├── docs/
│   │   └── mino-api-integration.md
│   ├── hooks/
│   │   ├── use-game-search.ts
│   │   ├── use-mobile.ts
│   │   └── use-toast.ts
│   ├── lib/
│   │   ├── types.ts
│   │   └── utils.ts
│   ├── next.config.mjs
│   ├── package.json
│   ├── postcss.config.mjs
│   ├── styles/
│   │   └── globals.css
│   └── tsconfig.json
├── golden-images.yaml
├── lego-hunter/
│   ├── .env.example
│   ├── .gitignore
│   ├── README.md
│   ├── app/
│   │   ├── api/
│   │   │   ├── generate-urls/
│   │   │   │   └── route.ts
│   │   │   └── search-lego/
│   │   │       └── route.ts
│   │   ├── globals.css
│   │   ├── layout.tsx
│   │   └── page.tsx
│   ├── components/
│   │   ├── best-deal-card.tsx
│   │   ├── browser-preview.tsx
│   │   ├── lego-confetti.tsx
│   │   ├── results-table.tsx
│   │   ├── retailer-card.tsx
│   │   └── ui/
│   │       ├── button.tsx
│   │       ├── card.tsx
│   │       └── input.tsx
│   ├── components.json
│   ├── eslint.config.mjs
│   ├── lib/
│   │   ├── gemini-client.ts
│   │   ├── retailers.ts
│   │   └── utils.ts
│   ├── next.config.ts
│   ├── package.json
│   ├── postcss.config.mjs
│   ├── tsconfig.json
│   └── types/
│       └── index.ts
├── loan-decision-copilot/
│   ├── README.md
│   ├── docs/
│   │   └── MINO_API_DOCUMENTATION (1).md
│   ├── package.json
│   ├── public/
│   │   └── robots.txt
│   ├── src/
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── AgentCard.tsx
│   │   │   ├── BankDetailPanel.tsx
│   │   │   ├── LiveBrowserPreview.tsx
│   │   │   ├── LoanTypeSelector.tsx
│   │   │   ├── LocationInput.tsx
│   │   │   ├── NavLink.tsx
│   │   │   ├── SearchProgress.tsx
│   │   │   └── ui/
│   │   │       ├── avatar.tsx
│   │   │       ├── button.tsx
│   │   │       └── card.tsx
│   │   ├── hooks/
│   │   │   └── useLoanSearch.ts
│   │   ├── integrations/
│   │   │   └── supabase/
│   │   │       ├── client.ts
│   │   │       └── types.ts
│   │   ├── lib/
│   │   │   └── utils.ts
│   │   ├── pages/
│   │   │   ├── Index.tsx
│   │   │   └── NotFound.tsx
│   │   └── types/
│   │       └── loan.ts
│   ├── supabase/
│   │   ├── config.toml
│   │   └── functions/
│   │       ├── analyze-loan/
│   │       │   └── index.ts
│   │       └── discover-banks/
│   │           └── index.ts
│   └── tsconfig.json
├── logistics-sentry/
│   ├── README.md
│   ├── jsconfig.json
│   ├── next.config.js
│   ├── package.json
│   ├── postcss.config.js
│   ├── src/
│   │   ├── app/
│   │   │   ├── api/
│   │   │   │   ├── agent/
│   │   │   │   │   └── run/
│   │   │   │   │       └── route.js
│   │   │   │   ├── logistics/
│   │   │   │   │   └── risk-assessment/
│   │   │   │   │       └── route.js
│   │   │   │   └── pricing/
│   │   │   │       └── run/
│   │   │   │           └── route.js
│   │   │   ├── competitive-pricing/
│   │   │   │   └── page.js
│   │   │   ├── globals.css
│   │   │   ├── layout.js
│   │   │   └── page.js
│   │   ├── components/
│   │   │   ├── ActionPanel.js
│   │   │   ├── ActivityFeed.js
│   │   │   ├── AgentHeader.js
│   │   │   ├── DecisionReasoning.js
│   │   │   ├── InventoryAlert.js
│   │   │   ├── InventoryInput.js
│   │   │   ├── LiveStream.js
│   │   │   ├── MetricCard.js
│   │   │   ├── RiskAssessment.js
│   │   │   └── TinyFishAgentAesthetics.js
│   │   ├── hooks/
│   │   │   └── use-toast.js
│   │   └── lib/
│   │       ├── decision-engine.js
│   │       ├── logistics/
│   │       │   └── agent.js
│   │       ├── pricing-intelligence.js
│   │       ├── tinyfish.js
│   │       └── utils.js
│   └── tailwind.config.js
├── openbox-deals/
│   ├── .gitignore
│   ├── README.md
│   ├── app/
│   │   ├── __init__.py
│   │   └── main.py
│   ├── railway.toml
│   ├── requirements.txt
│   └── static/
│       └── index.html
├── renovate.json
├── research-sentry/
│   ├── .gitignore
│   ├── README.md
│   ├── app/
│   │   ├── api/
│   │   │   ├── citations/
│   │   │   │   └── track/
│   │   │   │       └── route.ts
│   │   │   ├── compare/
│   │   │   │   └── route.ts
│   │   │   ├── conversation/
│   │   │   │   └── route.ts
│   │   │   ├── emails/
│   │   │   │   └── extract/
│   │   │   │       └── route.ts
│   │   │   ├── export/
│   │   │   │   └── bibtex/
│   │   │   │       └── route.ts
│   │   │   ├── health/
│   │   │   │   └── route.ts
│   │   │   ├── search/
│   │   │   │   ├── text/
│   │   │   │   │   └── route.ts
│   │   │   │   └── voice/
│   │   │   │       └── route.ts
│   │   │   └── summarize/
│   │   │       └── route.ts
│   │   ├── globals.css
│   │   ├── layout.tsx
│   │   └── page.tsx
│   ├── components/
│   │   ├── AudioPlayer.tsx
│   │   ├── CitationTracker.tsx
│   │   ├── CoPilotMode.tsx
│   │   ├── ConversationInterface.tsx
│   │   ├── ErrorMessage.tsx
│   │   ├── LoadingSpinner.tsx
│   │   ├── PaperCard.tsx
│   │   ├── PaperComparison.tsx
│   │   ├── PaperSummary.tsx
│   │   ├── ResultsGrid.tsx
│   │   ├── SearchInterface.tsx
│   │   ├── TinyFishAgentTerminal.tsx
│   │   ├── VoiceRecorder.tsx
│   │   └── WorkflowSelector.tsx
│   ├── hooks/
│   │   └── useVoiceCommands.ts
│   ├── lib/
│   │   ├── aggregator.ts
│   │   ├── audio-utils.ts
│   │   ├── citation-tracker.ts
│   │   ├── comparator.ts
│   │   ├── conversation.ts
│   │   ├── email-utils.ts
│   │   ├── intent-parser.ts
│   │   ├── mino.ts
│   │   ├── pdf-utils.ts
│   │   ├── search.ts
│   │   ├── summarizer.ts
│   │   ├── types.ts
│   │   ├── whisper.ts
│   │   └── workflows.ts
│   ├── next-env.d.ts
│   ├── next.config.js
│   ├── package.json
│   ├── postcss.config.js
│   ├── tailwind.config.ts
│   ├── tsconfig.json
│   ├── vercel.json
│   └── voice-research-project.txt
├── restaurant-comparison-tool/
│   ├── .gitignore
│   ├── README.md
│   ├── components.json
│   ├── eslint.config.js
│   ├── index.html
│   ├── package.json
│   ├── src/
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── layout/
│   │   │   │   ├── Footer.tsx
│   │   │   │   └── Header.tsx
│   │   │   ├── live/
│   │   │   │   ├── AgentStatusIndicator.tsx
│   │   │   │   ├── LiveBrowserPreview.tsx
│   │   │   │   ├── LiveSearchPanel.tsx
│   │   │   │   └── RestaurantAgentCard.tsx
│   │   │   ├── results/
│   │   │   │   ├── AgentLoadingCard.tsx
│   │   │   │   ├── AllergenRiskBadge.tsx
│   │   │   │   ├── AllergenRiskPanel.tsx
│   │   │   │   ├── ComparisonDashboard.tsx
│   │   │   │   ├── ConfidenceIndicator.tsx
│   │   │   │   ├── FitExplanation.tsx
│   │   │   │   ├── GoogleMapsLink.tsx
│   │   │   │   ├── RestaurantResultCard.tsx
│   │   │   │   ├── ResultDetailPanel.tsx
│   │   │   │   └── SafetyScoreRing.tsx
│   │   │   ├── search/
│   │   │   │   ├── AllergenSelector.tsx
│   │   │   │   ├── PreferenceSelector.tsx
│   │   │   │   ├── RestaurantInput.tsx
│   │   │   │   └── SearchForm.tsx
│   │   │   └── ui/
│   │   │       ├── badge.tsx
│   │   │       ├── button.tsx
│   │   │       ├── card.tsx
│   │   │       ├── dialog.tsx
│   │   │       ├── input.tsx
│   │   │       ├── scroll-area.tsx
│   │   │       ├── select.tsx
│   │   │       ├── separator.tsx
│   │   │       ├── skeleton.tsx
│   │   │       ├── toggle.tsx
│   │   │       └── tooltip.tsx
│   │   ├── context/
│   │   │   └── SearchContext.tsx
│   │   ├── hooks/
│   │   │   └── useRestaurantSearch.ts
│   │   ├── index.css
│   │   ├── lib/
│   │   │   ├── allergens.ts
│   │   │   ├── constants.ts
│   │   │   ├── goal-builder.ts
│   │   │   ├── score-calculator.ts
│   │   │   ├── tinyfish-client.ts
│   │   │   └── utils.ts
│   │   ├── main.tsx
│   │   └── types/
│   │       └── index.ts
│   ├── tsconfig.app.json
│   ├── tsconfig.json
│   ├── tsconfig.node.json
│   └── vite.config.ts
├── scholarship-finder/
│   ├── .env.example
│   ├── .gitignore
│   ├── README.md
│   ├── bun.lockb
│   ├── components.json
│   ├── eslint.config.js
│   ├── index.html
│   ├── package.json
│   ├── postcss.config.js
│   ├── public/
│   │   └── robots.txt
│   ├── src/
│   │   ├── App.css
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── CompareButton.tsx
│   │   │   ├── CompareDashboard.tsx
│   │   │   ├── Header.tsx
│   │   │   ├── LoadingAnimation.tsx
│   │   │   ├── NavLink.tsx
│   │   │   ├── ScholarshipCard.tsx
│   │   │   ├── SearchForm.tsx
│   │   │   ├── SearchResults.tsx
│   │   │   ├── SelectableScholarshipCard.tsx
│   │   │   └── ui/
│   │   │       ├── accordion.tsx
│   │   │       ├── alert-dialog.tsx
│   │   │       ├── alert.tsx
│   │   │       ├── aspect-ratio.tsx
│   │   │       ├── avatar.tsx
│   │   │       ├── badge.tsx
│   │   │       ├── breadcrumb.tsx
│   │   │       ├── button.tsx
│   │   │       ├── calendar.tsx
│   │   │       ├── card.tsx
│   │   │       ├── carousel.tsx
│   │   │       ├── chart.tsx
│   │   │       ├── checkbox.tsx
│   │   │       ├── collapsible.tsx
│   │   │       ├── command.tsx
│   │   │       ├── context-menu.tsx
│   │   │       ├── dialog.tsx
│   │   │       ├── drawer.tsx
│   │   │       ├── dropdown-menu.tsx
│   │   │       ├── form.tsx
│   │   │       ├── hover-card.tsx
│   │   │       ├── input-otp.tsx
│   │   │       ├── input.tsx
│   │   │       ├── label.tsx
│   │   │       ├── menubar.tsx
│   │   │       ├── navigation-menu.tsx
│   │   │       ├── pagination.tsx
│   │   │       ├── popover.tsx
│   │   │       ├── progress.tsx
│   │   │       ├── radio-group.tsx
│   │   │       ├── resizable.tsx
│   │   │       ├── scroll-area.tsx
│   │   │       ├── select.tsx
│   │   │       ├── separator.tsx
│   │   │       ├── sheet.tsx
│   │   │       ├── sidebar.tsx
│   │   │       ├── skeleton.tsx
│   │   │       ├── slider.tsx
│   │   │       ├── sonner.tsx
│   │   │       ├── switch.tsx
│   │   │       ├── table.tsx
│   │   │       ├── tabs.tsx
│   │   │       ├── textarea.tsx
│   │   │       ├── toast.tsx
│   │   │       ├── toaster.tsx
│   │   │       ├── toggle-group.tsx
│   │   │       ├── toggle.tsx
│   │   │       ├── tooltip.tsx
│   │   │       └── use-toast.ts
│   │   ├── hooks/
│   │   │   ├── use-mobile.tsx
│   │   │   ├── use-toast.ts
│   │   │   └── useScholarshipSearch.ts
│   │   ├── index.css
│   │   ├── integrations/
│   │   │   └── supabase/
│   │   │       ├── client.ts
│   │   │       └── types.ts
│   │   ├── lib/
│   │   │   └── utils.ts
│   │   ├── main.tsx
│   │   ├── pages/
│   │   │   ├── Index.tsx
│   │   │   └── NotFound.tsx
│   │   ├── test/
│   │   │   ├── example.test.ts
│   │   │   └── setup.ts
│   │   ├── types/
│   │   │   └── scholarship.ts
│   │   └── vite-env.d.ts
│   ├── supabase/
│   │   ├── config.toml
│   │   └── functions/
│   │       └── search-scholarships/
│   │           └── index.ts
│   ├── tailwind.config.ts
│   ├── tsconfig.app.json
│   ├── tsconfig.json
│   ├── tsconfig.node.json
│   ├── vite.config.ts
│   └── vitest.config.ts
├── silicon-signal/
│   ├── .env.example
│   ├── .gitignore
│   ├── README.md
│   ├── data/
│   │   └── history.json
│   ├── eslint.config.mjs
│   ├── next.config.ts
│   ├── package.json
│   ├── postcss.config.mjs
│   ├── src/
│   │   ├── app/
│   │   │   ├── api/
│   │   │   │   └── scan/
│   │   │   │       └── route.ts
│   │   │   ├── globals.css
│   │   │   ├── layout.tsx
│   │   │   └── page.tsx
│   │   ├── components/
│   │   │   ├── Dashboard.tsx
│   │   │   ├── Header.tsx
│   │   │   ├── HistoricalTrend.tsx
│   │   │   ├── PlatformView.tsx
│   │   │   ├── RiskBadge.tsx
│   │   │   ├── ScanForm.tsx
│   │   │   ├── ScanResultCard.tsx
│   │   │   ├── SignalOverview.tsx
│   │   │   ├── SiliconWafer.tsx
│   │   │   ├── SystemArchitecture.tsx
│   │   │   └── VendorIntelligence.tsx
│   │   ├── lib/
│   │   │   └── store.ts
│   │   └── types.ts
│   └── tsconfig.json
├── skills/
│   └── use-tinyfish/
│       └── SKILL.md
├── stay-scout-hub/
│   ├── README.md
│   ├── docs/
│   │   └── MINO_AREA_RESEARCH_API.md
│   ├── package.json
│   ├── public/
│   │   └── robots.txt
│   ├── src/
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── AreaCard.tsx
│   │   │   ├── AreaResultsSection.tsx
│   │   │   ├── HeroSection.tsx
│   │   │   ├── LiveBrowserPreview.tsx
│   │   │   ├── NavLink.tsx
│   │   │   ├── PlatformCard.tsx
│   │   │   ├── PurposeSelector.tsx
│   │   │   ├── ResultsSection.tsx
│   │   │   ├── SearchFormV2.tsx
│   │   │   └── ui/
│   │   │       ├── button.tsx
│   │   │       └── card.tsx
│   │   ├── hooks/
│   │   │   ├── useAreaSearch.ts
│   │   │   └── useHotelSearch.ts
│   │   ├── integrations/
│   │   │   └── supabase/
│   │   │       ├── client.ts
│   │   │       └── types.ts
│   │   ├── lib/
│   │   │   ├── api/
│   │   │   │   ├── area-search.ts
│   │   │   │   └── hotel-search.ts
│   │   │   └── utils.ts
│   │   ├── pages/
│   │   │   ├── Index.tsx
│   │   │   └── NotFound.tsx
│   │   └── types/
│   │       └── hotel.ts
│   ├── supabase/
│   │   ├── config.toml
│   │   └── functions/
│   │       ├── check-platform/
│   │       │   └── index.ts
│   │       ├── discover-areas/
│   │       │   └── index.ts
│   │       ├── discover-platforms/
│   │       │   └── index.ts
│   │       └── reasearch-area/
│   │           └── index.ts
│   └── tsconfig.json
├── summer-school-finder/
│   ├── README.md
│   ├── bun.lockb
│   ├── components.json
│   ├── eslint.config.js
│   ├── index.html
│   ├── package.json
│   ├── postcss.config.js
│   ├── public/
│   │   └── robots.txt
│   ├── src/
│   │   ├── App.css
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── AgentCard.tsx
│   │   │   ├── CompareModal.tsx
│   │   │   ├── LiveAgentCard.tsx
│   │   │   ├── NavLink.tsx
│   │   │   ├── ResultCard.tsx
│   │   │   ├── SearchForm.tsx
│   │   │   └── ui/
│   │   │       ├── accordion.tsx
│   │   │       ├── alert-dialog.tsx
│   │   │       ├── alert.tsx
│   │   │       ├── aspect-ratio.tsx
│   │   │       ├── avatar.tsx
│   │   │       ├── badge.tsx
│   │   │       ├── breadcrumb.tsx
│   │   │       ├── button.tsx
│   │   │       ├── calendar.tsx
│   │   │       ├── card.tsx
│   │   │       ├── carousel.tsx
│   │   │       ├── chart.tsx
│   │   │       ├── checkbox.tsx
│   │   │       ├── collapsible.tsx
│   │   │       ├── command.tsx
│   │   │       ├── context-menu.tsx
│   │   │       ├── dialog.tsx
│   │   │       ├── drawer.tsx
│   │   │       ├── dropdown-menu.tsx
│   │   │       ├── form.tsx
│   │   │       ├── hover-card.tsx
│   │   │       ├── input-otp.tsx
│   │   │       ├── input.tsx
│   │   │       ├── label.tsx
│   │   │       ├── menubar.tsx
│   │   │       ├── navigation-menu.tsx
│   │   │       ├── pagination.tsx
│   │   │       ├── popover.tsx
│   │   │       ├── progress.tsx
│   │   │       ├── radio-group.tsx
│   │   │       ├── resizable.tsx
│   │   │       ├── scroll-area.tsx
│   │   │       ├── select.tsx
│   │   │       ├── separator.tsx
│   │   │       ├── sheet.tsx
│   │   │       ├── sidebar.tsx
│   │   │       ├── skeleton.tsx
│   │   │       ├── slider.tsx
│   │   │       ├── sonner.tsx
│   │   │       ├── switch.tsx
│   │   │       ├── table.tsx
│   │   │       ├── tabs.tsx
│   │   │       ├── textarea.tsx
│   │   │       ├── toast.tsx
│   │   │       ├── toaster.tsx
│   │   │       ├── toggle-group.tsx
│   │   │       ├── toggle.tsx
│   │   │       ├── tooltip.tsx
│   │   │       └── use-toast.ts
│   │   ├── hooks/
│   │   │   ├── use-mobile.tsx
│   │   │   ├── use-toast.ts
│   │   │   └── useSummerSchoolSearch.ts
│   │   ├── index.css
│   │   ├── integrations/
│   │   │   └── supabase/
│   │   │       ├── client.ts
│   │   │       └── types.ts
│   │   ├── lib/
│   │   │   └── utils.ts
│   │   ├── main.tsx
│   │   ├── pages/
│   │   │   ├── Index.tsx
│   │   │   └── NotFound.tsx
│   │   ├── test/
│   │   │   ├── example.test.ts
│   │   │   └── setup.ts
│   │   ├── types/
│   │   │   └── summer-school.ts
│   │   └── vite-env.d.ts
│   ├── supabase/
│   │   ├── config.toml
│   │   └── functions/
│   │       ├── discover-schools/
│   │       │   └── index.ts
│   │       ├── mino-search/
│   │       │   └── index.ts
│   │       └── mino-search-stream/
│   │           └── index.ts
│   ├── tailwind.config.ts
│   ├── tsconfig.app.json
│   ├── tsconfig.json
│   ├── tsconfig.node.json
│   ├── vite.config.ts
│   └── vitest.config.ts
├── tenders-finder/
│   ├── .env.example
│   ├── .gitignore
│   ├── README.md
│   ├── bun.lockb
│   ├── components.json
│   ├── eslint.config.js
│   ├── index.html
│   ├── package.json
│   ├── postcss.config.js
│   ├── public/
│   │   └── robots.txt
│   ├── src/
│   │   ├── App.css
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── NavLink.tsx
│   │   │   ├── tender/
│   │   │   │   ├── AgentPreviewCard.tsx
│   │   │   │   ├── AgentPreviewGrid.tsx
│   │   │   │   ├── CompareButton.tsx
│   │   │   │   ├── CompareModal.tsx
│   │   │   │   ├── Header.tsx
│   │   │   │   ├── LinkConfigPage.tsx
│   │   │   │   ├── LiveBrowserModal.tsx
│   │   │   │   ├── SectorIcon.tsx
│   │   │   │   ├── SectorSelector.tsx
│   │   │   │   ├── TenderResultCard.tsx
│   │   │   │   └── TenderResultsList.tsx
│   │   │   └── ui/
│   │   │       ├── accordion.tsx
│   │   │       ├── alert-dialog.tsx
│   │   │       ├── alert.tsx
│   │   │       ├── aspect-ratio.tsx
│   │   │       ├── avatar.tsx
│   │   │       ├── badge.tsx
│   │   │       ├── breadcrumb.tsx
│   │   │       ├── button.tsx
│   │   │       ├── calendar.tsx
│   │   │       ├── card.tsx
│   │   │       ├── carousel.tsx
│   │   │       ├── chart.tsx
│   │   │       ├── checkbox.tsx
│   │   │       ├── collapsible.tsx
│   │   │       ├── command.tsx
│   │   │       ├── context-menu.tsx
│   │   │       ├── dialog.tsx
│   │   │       ├── drawer.tsx
│   │   │       ├── dropdown-menu.tsx
│   │   │       ├── form.tsx
│   │   │       ├── hover-card.tsx
│   │   │       ├── input-otp.tsx
│   │   │       ├── input.tsx
│   │   │       ├── label.tsx
│   │   │       ├── menubar.tsx
│   │   │       ├── navigation-menu.tsx
│   │   │       ├── pagination.tsx
│   │   │       ├── popover.tsx
│   │   │       ├── progress.tsx
│   │   │       ├── radio-group.tsx
│   │   │       ├── resizable.tsx
│   │   │       ├── scroll-area.tsx
│   │   │       ├── select.tsx
│   │   │       ├── separator.tsx
│   │   │       ├── sheet.tsx
│   │   │       ├── sidebar.tsx
│   │   │       ├── skeleton.tsx
│   │   │       ├── slider.tsx
│   │   │       ├── sonner.tsx
│   │   │       ├── switch.tsx
│   │   │       ├── table.tsx
│   │   │       ├── tabs.tsx
│   │   │       ├── textarea.tsx
│   │   │       ├── toast.tsx
│   │   │       ├── toaster.tsx
│   │   │       ├── toggle-group.tsx
│   │   │       ├── toggle.tsx
│   │   │       ├── tooltip.tsx
│   │   │       └── use-toast.ts
│   │   ├── hooks/
│   │   │   ├── use-mobile.tsx
│   │   │   ├── use-toast.ts
│   │   │   └── useTenderSearch.ts
│   │   ├── index.css
│   │   ├── integrations/
│   │   │   └── supabase/
│   │   │       ├── client.ts
│   │   │       └── types.ts
│   │   ├── lib/
│   │   │   ├── api/
│   │   │   │   └── tinyfish.ts
│   │   │   └── utils.ts
│   │   ├── main.tsx
│   │   ├── pages/
│   │   │   ├── Index.tsx
│   │   │   └── NotFound.tsx
│   │   ├── test/
│   │   │   └── setup.ts
│   │   ├── types/
│   │   │   └── tender.ts
│   │   └── vite-env.d.ts
│   ├── supabase/
│   │   ├── config.toml
│   │   └── functions/
│   │       ├── discover-tender-links/
│   │       │   └── index.ts
│   │       └── tinyfish-tender-search/
│   │           └── index.ts
│   ├── tailwind.config.ts
│   ├── tsconfig.app.json
│   ├── tsconfig.json
│   ├── tsconfig.node.json
│   ├── vite.config.ts
│   └── vitest.config.ts
├── tinyskills/
│   ├── .gitignore
│   ├── README.md
│   ├── app/
│   │   ├── api/
│   │   │   ├── identify-sources/
│   │   │   │   └── route.ts
│   │   │   ├── scrape-sources/
│   │   │   │   └── route.ts
│   │   │   └── synthesize/
│   │   │       └── route.ts
│   │   ├── globals.css
│   │   ├── history/
│   │   │   └── page.tsx
│   │   ├── layout.tsx
│   │   ├── page.tsx
│   │   └── settings/
│   │       └── page.tsx
│   ├── components/
│   │   ├── skillforge/
│   │   │   ├── nav-header.tsx
│   │   │   ├── phase-indicator.tsx
│   │   │   ├── skill-output.tsx
│   │   │   ├── skill-preview.tsx
│   │   │   ├── skill-raw.tsx
│   │   │   ├── skill-sources.tsx
│   │   │   ├── source-progress.tsx
│   │   │   ├── source-toggles.tsx
│   │   │   └── terminal-input.tsx
│   │   └── ui/
│   │       ├── badge.tsx
│   │       ├── button.tsx
│   │       ├── card.tsx
│   │       ├── input.tsx
│   │       ├── progress.tsx
│   │       ├── scroll-area.tsx
│   │       ├── sonner.tsx
│   │       ├── switch.tsx
│   │       ├── tabs.tsx
│   │       └── tooltip.tsx
│   ├── components.json
│   ├── eslint.config.mjs
│   ├── hooks/
│   │   ├── use-generation.ts
│   │   └── use-local-storage.ts
│   ├── lib/
│   │   ├── ai-client.ts
│   │   ├── mino-client.ts
│   │   ├── storage.ts
│   │   └── utils.ts
│   ├── next.config.ts
│   ├── package.json
│   ├── postcss.config.mjs
│   ├── public/
│   │   └── tinyfish.avif
│   ├── tsconfig.json
│   └── types/
│       └── index.ts
├── tutor-finder/
│   ├── .env.example
│   ├── .gitignore
│   ├── README.md
│   ├── bun.lockb
│   ├── components.json
│   ├── eslint.config.js
│   ├── index.html
│   ├── package.json
│   ├── postcss.config.js
│   ├── public/
│   │   └── robots.txt
│   ├── src/
│   │   ├── App.css
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── AgentPreviewCard.tsx
│   │   │   ├── AgentPreviewGrid.tsx
│   │   │   ├── CompareButton.tsx
│   │   │   ├── CompareDashboard.tsx
│   │   │   ├── DiscoveringState.tsx
│   │   │   ├── ExamSelector.tsx
│   │   │   ├── LocationInput.tsx
│   │   │   ├── NavLink.tsx
│   │   │   ├── TutorCard.tsx
│   │   │   ├── TutorResultsGrid.tsx
│   │   │   └── ui/
│   │   │       ├── accordion.tsx
│   │   │       ├── alert-dialog.tsx
│   │   │       ├── alert.tsx
│   │   │       ├── aspect-ratio.tsx
│   │   │       ├── avatar.tsx
│   │   │       ├── badge.tsx
│   │   │       ├── breadcrumb.tsx
│   │   │       ├── button.tsx
│   │   │       ├── calendar.tsx
│   │   │       ├── card.tsx
│   │   │       ├── carousel.tsx
│   │   │       ├── chart.tsx
│   │   │       ├── checkbox.tsx
│   │   │       ├── collapsible.tsx
│   │   │       ├── command.tsx
│   │   │       ├── context-menu.tsx
│   │   │       ├── dialog.tsx
│   │   │       ├── drawer.tsx
│   │   │       ├── dropdown-menu.tsx
│   │   │       ├── form.tsx
│   │   │       ├── hover-card.tsx
│   │   │       ├── input-otp.tsx
│   │   │       ├── input.tsx
│   │   │       ├── label.tsx
│   │   │       ├── menubar.tsx
│   │   │       ├── navigation-menu.tsx
│   │   │       ├── pagination.tsx
│   │   │       ├── popover.tsx
│   │   │       ├── progress.tsx
│   │   │       ├── radio-group.tsx
│   │   │       ├── resizable.tsx
│   │   │       ├── scroll-area.tsx
│   │   │       ├── select.tsx
│   │   │       ├── separator.tsx
│   │   │       ├── sheet.tsx
│   │   │       ├── sidebar.tsx
│   │   │       ├── skeleton.tsx
│   │   │       ├── slider.tsx
│   │   │       ├── sonner.tsx
│   │   │       ├── switch.tsx
│   │   │       ├── table.tsx
│   │   │       ├── tabs.tsx
│   │   │       ├── textarea.tsx
│   │   │       ├── toast.tsx
│   │   │       ├── toaster.tsx
│   │   │       ├── toggle-group.tsx
│   │   │       ├── toggle.tsx
│   │   │       ├── tooltip.tsx
│   │   │       └── use-toast.ts
│   │   ├── hooks/
│   │   │   ├── use-mobile.tsx
│   │   │   ├── use-toast.ts
│   │   │   └── useTutorSearch.ts
│   │   ├── index.css
│   │   ├── integrations/
│   │   │   └── supabase/
│   │   │       ├── client.ts
│   │   │       └── types.ts
│   │   ├── lib/
│   │   │   └── utils.ts
│   │   ├── main.tsx
│   │   ├── pages/
│   │   │   ├── Index.tsx
│   │   │   └── NotFound.tsx
│   │   ├── test/
│   │   │   ├── example.test.ts
│   │   │   └── setup.ts
│   │   ├── types/
│   │   │   └── tutor.ts
│   │   └── vite-env.d.ts
│   ├── supabase/
│   │   ├── config.toml
│   │   └── functions/
│   │       ├── discover-tutor-websites/
│   │       │   └── index.ts
│   │       └── search-tutors-mino/
│   │           └── index.ts
│   ├── tailwind.config.ts
│   ├── tsconfig.app.json
│   ├── tsconfig.json
│   ├── tsconfig.node.json
│   ├── vite.config.ts
│   └── vitest.config.ts
├── viet-bike-scout/
│   ├── .env.example
│   ├── .gitignore
│   ├── README.md
│   ├── components.json
│   ├── docs/
│   │   ├── PRD.md
│   │   └── use-case-brief.md
│   ├── eslint.config.mjs
│   ├── next.config.ts
│   ├── package.json
│   ├── postcss.config.mjs
│   ├── src/
│   │   ├── app/
│   │   │   ├── api/
│   │   │   │   └── search/
│   │   │   │       └── route.ts
│   │   │   ├── globals.css
│   │   │   ├── layout.tsx
│   │   │   └── page.tsx
│   │   ├── components/
│   │   │   ├── bike-card.tsx
│   │   │   ├── live-preview-grid.tsx
│   │   │   ├── results-grid.tsx
│   │   │   ├── shop-group.tsx
│   │   │   └── ui/
│   │   │       ├── badge.tsx
│   │   │       ├── button.tsx
│   │   │       ├── card.tsx
│   │   │       ├── skeleton.tsx
│   │   │       └── switch.tsx
│   │   ├── hooks/
│   │   │   └── use-bike-search.ts
│   │   └── lib/
│   │       ├── env.ts
│   │       ├── supabase.ts
│   │       └── utils.ts
│   ├── supabase/
│   │   ├── .gitignore
│   │   ├── config.toml
│   │   └── migrations/
│   │       └── 20260224170438_create_bike_cache.sql
│   └── tsconfig.json
├── waifu-deal-sniper/
│   ├── .gitignore
│   ├── README.md
│   ├── bot.js
│   ├── database.js
│   ├── package.json
│   └── templates.js
└── wing-command/
    ├── .eslintrc.json
    ├── .gitignore
    ├── README.md
    ├── app/
    │   ├── api/
    │   │   ├── deals/
    │   │   │   └── route.ts
    │   │   ├── menu/
    │   │   │   └── route.ts
    │   │   └── scout/
    │   │       └── route.ts
    │   ├── error.tsx
    │   ├── global-error.tsx
    │   ├── globals.css
    │   ├── layout.tsx
    │   ├── loading.tsx
    │   └── page.tsx
    ├── components/
    │   ├── AnimatedFieldBackground.tsx
    │   ├── BannerBreak.tsx
    │   ├── CoachHero.tsx
    │   ├── CoachWingMascot.tsx
    │   ├── CoinToss.tsx
    │   ├── ComicHero.tsx
    │   ├── CommandJumbotron.tsx
    │   ├── CompareBar.tsx
    │   ├── CompareModal.tsx
    │   ├── DealsView.tsx
    │   ├── FlavorSelector.tsx
    │   ├── FlavorTarot.tsx
    │   ├── FrostedGlassPanel.tsx
    │   ├── GlassBlitzEntrance.tsx
    │   ├── HeroVisuals.tsx
    │   ├── JumbotronSearch.tsx
    │   ├── MenuModal.tsx
    │   ├── PlaybookSearch.tsx
    │   ├── ScoutingReportCard.tsx
    │   ├── SunnyFieldEntrance.tsx
    │   ├── TacticalCanvas.tsx
    │   ├── TradingCardGrid.tsx
    │   ├── TrashTalkTicker.tsx
    │   ├── WingGrid.tsx
    │   ├── ZipSearch.tsx
    │   └── ui/
    │       ├── Accordion.tsx
    │       ├── Badge.tsx
    │       ├── Button.tsx
    │       ├── Input.tsx
    │       ├── Sheet.tsx
    │       └── index.ts
    ├── lib/
    │   ├── cache.ts
    │   ├── chain-prices.ts
    │   ├── deals.ts
    │   ├── env.ts
    │   ├── geocode.ts
    │   ├── menu.ts
    │   ├── seed-data.ts
    │   ├── supabase.ts
    │   ├── tinyfish-scraper.ts
    │   ├── types.ts
    │   └── utils.ts
    ├── next.config.mjs
    ├── package.json
    ├── postcss.config.js
    ├── render.yaml
    ├── scraper/
    │   ├── requirements.txt
    │   └── scrape_wings.py
    ├── scripts/
    │   └── cache-warmer.ts
    ├── supabase/
    │   └── schema.sql
    ├── tailwind.config.ts
    └── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/CODEOWNERS
================================================
# This file is managed by Terraform in github-control repository
# Do not edit this file, all changes will be overwritten
# If you need to change this file, create a pull request in
# https://github.com/tinyfish-io/github-control

.github/workflows/secrets-scanner.yml @tinyfish-io/security_team
.github/workflows/vuln-scanner-pr.yml @tinyfish-io/security_team
.semgrepignore @tinyfish-io/security_team
osv-scanner.toml @tinyfish-io/security_team


================================================
FILE: .github/config/.pre-commit-config-template.yaml
================================================
---
repos:
  - repo: "local"
    hooks:
      - id: "trufflehog"
        name: "TruffleHog"
        description: Detect secrets in your data.
        entry: bash -c 'trufflehog git file://. --since-commit HEAD --results=verified,unknown --fail'
        language: system
        stages: ["pre-commit", "pre-push"]


================================================
FILE: .github/workflows/secrets-scanner.yml
================================================
# This file is managed by Terraform in github-control repository
# Do not edit this file, all changes will be overwritten
# If you need to change this file, create a pull request in
# https://github.com/tinyfish-io/github-control
---
name: Leaked Secrets Scan
on: # yamllint disable-line rule:truthy
  pull_request:
  merge_group:
    branches: [main]

jobs:
  TruffleHog:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v6
        with:
          fetch-depth: 0
      - name: TruffleHog OSS
        uses: trufflesecurity/trufflehog@main
        with:
          path: ./
          base: ${{ github.event.repository.default_branch }}
          head: HEAD
          extra_args: --only-verified


================================================
FILE: .github/workflows/vuln-scanner-pr.yml
================================================
---
name: OSV-Scanner PR Scan

on:  # yamllint disable-line rule:truthy
  pull_request:
    branches: [main]

permissions:
  # Required to upload SARIF file to CodeQL. See: https://github.com/github/codeql-action/issues/2117
  actions: read
  # Require writing security events to upload SARIF file to security tab
  security-events: write
  # Only need to read contents
  contents: read

jobs:
  vulnerability-check:
    uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@v2.3.3"
    with:
      scan-args: --config osv-scanner.toml
      upload-sarif: false


================================================
FILE: .semgrepignore
================================================
# This file is managed by Terraform in github-control repository
# Do not edit this file, all changes will be overwritten
# If you need to change this file, create a pull request in
# https://github.com/tinyfish-io/github-control

# Exclude lock files from vulnerability scanning to avoid false positives
**/*.lock

# Exclude test files from vulnerability scanning
# Tests don't need security scanning as they are not production code
**/*test*/*
**/test.*
**/*.test.*
**/*.spec.*
**/*_test.*


================================================
FILE: .tags.json
================================================
{
  "repo-owner": "ENG",
  "vanta-dependabot-owner": "ENG",
  "vanta-ebs-inspector-owner": "ENG",
  "vanta-ecr-container-owner": "ENG"
}


================================================
FILE: .yamllint
================================================
# This file is managed by Terraform in github-control repository
# Do not edit this file, all changes will be overwritten
# If you need to change this file, create a pull request in
# https://github.com/tinyfish-io/github-control
---
extends: default

rules:
  line-length:
    max: 120
    level: warning
  comments:
    min-spaces-from-content: 1
    require-starting-space: false
  truthy: disable


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to the TinyFish Cookbook

Hello fellow coder! So you have chosen (or been compelled to) add your awesome mini use case to the TinyFish cookbook, here's some basics on how this cookbook is structured, and how to send in your Pull Request to make the process as smooth as possible.

## Repository Structure

Each project lives in its own folder at the root of this repo — no nesting, no hunting around. Just a flat collection of awesome things people have built with TinyFish.

```
TinyFish-cookbook/
├── .github/
├── brand-sentiment/
├── daily-briefing/
├── price-match/
├── sales-opportunity/
├── YOUR-NEW-PROJECT/        <--- This is you!
│
├── .gitignore
├── .semgrepignore
├── .tags.json
├── .yamllint
├── Makefile
├── README.md
├── CONTRIBUTING.md
└── renovate.json
```

> note: if your new to github, some of the steps below might seem a bit intimidating if your new to contributing to open source repos, but don't worry they become second nature after a while. And if this is your first time, we'd love to get one fo our engineers to hop on a call with you and guide you through! Hit us up on the [TinyFish Discord](https://discord.gg/tinyfish).

## Getting Started

1. Go ahead and fork the repo at https://github.com/tinyfish-io/TinyFish-cookbook
2. Clone this _forked version_ of the repo to your local computer
3. Create a new feature branch for your work (e.g., `git checkout -b {your-name}/cool-new-app`). **Avoid working directly on the `main` branch** to keep your fork clean.
4. Create a new folder at the root of the repo for your project and start coding!

> **Note:** If you need any help with the API, making the app, or anything at all, hit the TinyFish team up anytime at the [TinyFish Discord](https://discord.gg/tinyfish) (we'd love to help)

## Documentation!

Like Julius Caesar once [said](https://www.youtube.com/watch?v=xMHJGd3wwZk&list=RDxMHJGd3wwZk&start_radio=1), "I would have never conquered Rome if it wasn't for good documentation," hence, you too must write good documentation. To make things simple and consistent, we actually have a really easy template.

Each project folder **must** include a `README.md` with the following:

1. **Title**
2. **Live link**
3. **Short 2-3 liner about what your app is, and where the TinyFish API is used**
4. **Demo Video** *(gif or video format, whatever works best)*
5. **Snippet of your codebase that calls the TinyFish API** (the prompt can be truncated if its too long)
6. **How to Run the codebase** (declare any env vars that may be needed here)
7. **Architecture Diagram**

## Submitting Your Project

1. Remember to test your new app thoroughly, and make sure it's has a nice `README.md` as described in the above section
2. Push all your changes to your forked repo
3. Open a pull request, from your fork to the main TinyFish repo https://github.com/tinyfish-io/TinyFish-cookbook (main branch)
4. Sit tight! The very best TinyFish engineers will take a look, give you feedback and test your app!
5. If all's good! Your PR will be merged. Congrats! 🎉

Keep committing often and keep your code safe! And always remember, if you need anything or have any doubts, hit us up at the the [TinyFish Discord](https://discord.gg/tinyfish)


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2026 TinyFish

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: Makefile
================================================
SHELL := /usr/bin/env bash

include $(wildcard makefiles/*)

.PHONY: check-trufflehog
check-trufflehog:
	@if ! which trufflehog > /dev/null 2>&1; then \
		echo "TruffleHog is not installed."; \
		echo "MacOS users can install it with:"; \
		echo "  brew install trufflehog"; \
		echo ""; \
		echo "Linux users can install it with:"; \
		echo "  curl -sSfL https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/scripts/install.sh | sh -s -- -b /usr/local/bin"; \
		echo ""; \
		echo "For more details, go to https://github.com/trufflesecurity/trufflehog"; \
		exit 1; \
	fi

.PHONY: setup-pre-commit
setup-pre-commit:
	@if [ ! -f .pre-commit-config.yaml ]; then \
		echo ".pre-commit-config.yaml not found. Copying template..."; \
		cp .github/config/.pre-commit-config-template.yaml .pre-commit-config.yaml; \
		echo ".pre-commit-config.yaml created from template."; \
	else \
		echo ".pre-commit-config.yaml already exists."; \
	fi

.PHONY: init
init: setup-pre-commit check-trufflehog
	pip install pre-commit
	pre-commit install


================================================
FILE: Manga-Availability-Finder/README.md
================================================
# 🔍 Webtoon/Manga Availability Finder

**Live Demo:** [webtoonhunter.lovable.app](https://webtoonhunter.lovable.app)

---

## What is this?

Webtoon/Manga Availability Finder is an AI-powered manga/webtoon availability checker that searches multiple reading platforms simultaneously. It uses the TinyFish Web Agent API for real-time browser automation, deploying parallel browser agents to search and verify availability across multiple platforms.

---

## Demo

<!-- Replace with your demo gif/video -->

https://github.com/user-attachments/assets/7b3ef9be-d4ba-43be-b3b5-ed9ea246c591

---

## Code Snippet

```typescript
// Call TinyFish Web Agent API with SSE streaming for real-time browser automation

const response = await fetch("https://mino.ai/v1/automation/run-sse", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-API-Key": process.env.MINO_API_KEY,
  },
  body: JSON.stringify({
    url,  // e.g., "https://mangadex.org/search?q=One+Piece"
    goal: `You are searching for a manga/webtoon called "${mangaTitle}"...
           STEP 1: Find and use the search bar to enter the title
           STEP 2: Analyze the search results for matches
           STEP 3: Return JSON with { found: boolean, match_confidence: string }`,
    stream: true,
  }),
});

// Stream SSE events back to client for live preview
const reader = response.body?.getReader();
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  // Forward streamingUrl + completion events to frontend
}
```

---

## How to Run

### Prerequisites
- Node.js 18+
- Lovable Cloud account (or Supabase project)

### Environment Variables

| Variable | Description | Required |
|----------|-------------|----------|
| `MINO_API_KEY` | TinyFish Web Agent [API key](https://mino.ai) | ✅ |
| `GEMINI_API_KEY` | API key from [Google AI Studio](https://makersuite.google.com) | ✅ |

### Setup

```bash
# 1. Clone the repository
git clone <your-repo-url>
cd webtoon-hunter

# 2. Install dependencies
npm install

# 3. Add secrets to your Lovable Cloud / Supabase project
# Navigate to Settings → Secrets and add:
#   - TinyFish Web Agent AI KEY
#   - GEMINI_API_KEY

# 4. Start development server
npm run dev
```

---

## Architecture Diagram

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                              User Interface                                  │
│                                                                             │
│  ┌─────────────┐    ┌──────────────────┐    ┌─────────────────────────────┐ │
│  │ SearchHero  │───▶│  useMangaSearch  │───▶│  AgentCard (x6 parallel)   │ │
│  │  Component  │    │      Hook        │    │  with Live Stream Preview   │ │
│  └─────────────┘    └──────────────────┘    └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                         Edge Functions (Supabase)                            │
│                                                                             │
│  ┌────────────────────────┐         ┌────────────────────────────────────┐  │
│  │  discover-manga-sites  │         │         search-manga (x6)          │  │
│  │  (1x per search)       │         │     (parallel browser agents)      │  │
│  │                        │         │                                    │  │
│  │  Gemini → Get site URLs│         │  TinyFish API → Browser Automation |  |
│  │  (+ fallback sites)    │         │  (SSE real-time streaming)         │  │
│  └────────────────────────┘         └────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                            External APIs                                     │
│                                                                             │
│  ┌────────────────────────┐         ┌────────────────────────────────────┐  │
│  │      Gemini API        │         │      TinyFish Web Agent API        │  │
│  │   (Site Discovery)     │         │    (Browser Automation + SSE)      │  │
│  │      Called: 1x        │         │       Called: 5-6x parallel        │  │
│  └────────────────────────┘         └────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────────┘
```

### Flow Summary

1. **User enters manga title** → Client triggers search
2. **Gemini API** discovers 5-6 relevant manga site URLs (with fallback if rate-limited)
3. **TinyFish Web Agent API** deploys parallel browser agents to each site
4. **SSE Streaming** provides live browser preview URLs + real-time status updates
5. **Results** display which sites have the manga available

---

## Tech Stack

- **Frontend:** React, TypeScript, Tailwind CSS, shadcn/ui
- **Backend:** Supabase Edge Functions (Deno)
- **APIs:** TinyFish Web Agent (browser automation), Gemini (site discovery)
- **Streaming:** Server-Sent Events (SSE)

---

## License

MIT


================================================
FILE: Manga-Availability-Finder/docs/MINO_API_INTEGRATION.md
================================================
# Mino API Integration Documentation

## Product Architecture Overview

This application is a **Manga/Webtoon Finder** that uses AI-powered browser automation to search for manga availability across multiple websites simultaneously.

### System Architecture

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                              Client (React)                                  │
│                                                                             │
│  ┌─────────────┐    ┌──────────────────┐    ┌─────────────────────────────┐ │
│  │ SearchHero  │───▶│  useMangaSearch  │───▶│  AgentCard (x6 parallel)   │ │
│  │  Component  │    │      Hook        │    │  with Live Stream Preview   │ │
│  └─────────────┘    └──────────────────┘    └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                         Edge Functions (Supabase)                            │
│                                                                             │
│  ┌────────────────────────┐         ┌────────────────────────────────────┐  │
│  │  discover-manga-sites  │         │         search-manga (x6)          │  │
│  │  (Called: 1x)          │         │         (Called: 6x parallel)      │  │
│  │                        │         │                                    │  │
│  │  Gemini API → Get URLs │         │  Mino API → Browser Automation     │  │
│  │  (with fallback sites) │         │  (SSE Streaming)                   │  │
│  └────────────────────────┘         └────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                            External APIs                                     │
│                                                                             │
│  ┌────────────────────────┐         ┌────────────────────────────────────┐  │
│  │      Gemini API        │         │           Mino API                 │  │
│  │  (Site Discovery)      │         │    (Browser Automation + SSE)      │  │
│  └────────────────────────┘         └────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────────┘
```

### API Call Summary

| API | Purpose | Calls per Search | Response Type |
|-----|---------|------------------|---------------|
| Gemini API | Discover manga reading sites | 1x | JSON |
| Mino API | Automate browser search on each site | 5-6x (parallel) | SSE Stream |

### Orchestration Flow

1. **User enters manga title** → Client triggers `useMangaSearch.search(title)`
2. **Site Discovery** → `discover-manga-sites` edge function calls Gemini API (or uses fallback)
3. **Agent Initialization** → Client creates 5-6 agent cards in "idle" state
4. **Parallel Browser Automation** → `search-manga` edge function called for each site simultaneously
5. **Real-time Updates** → Mino SSE stream provides live browser preview URL + final result
6. **Results Display** → Each agent card updates independently as results arrive

---

## Code Snippets

### 1. Calling the Mino API (Edge Function)

```typescript
// supabase/functions/search-manga/index.ts

import { serve } from "https://deno.land/std@0.168.0/http/server.ts";

const corsHeaders = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
};

serve(async (req) => {
  if (req.method === "OPTIONS") {
    return new Response(null, { headers: corsHeaders });
  }

  const { url, mangaTitle } = await req.json();
  const MINO_API_KEY = Deno.env.get("MINO_API_KEY");

  // Define the automation goal (see Goal section below)
  const goal = `You are searching for a manga/webtoon called "${mangaTitle}"...`;

  // Call Mino API with SSE streaming
  const response = await fetch("https://mino.ai/v1/automation/run-sse", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-API-Key": MINO_API_KEY,
    },
    body: JSON.stringify({
      url,           // Starting URL (e.g., mangadex.org/search?q=One+Piece)
      goal,          // Natural language instruction for the browser agent
      timeout: 60000 // Maximum execution time in milliseconds
    }),
  });

  // Stream SSE events back to client
  // ... (see full implementation below)
});
```

### 2. Client-Side SSE Consumption

```typescript
// src/hooks/useMangaSearch.ts

const searchSite = async (agent: SiteAgent, title: string) => {
  const response = await fetch(`${supabaseUrl}/functions/v1/search-manga`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${supabaseKey}`,
      "apikey": supabaseKey,
    },
    body: JSON.stringify({ url: agent.siteUrl, mangaTitle: title }),
  });

  // Handle SSE stream
  const reader = response.body?.getReader();
  const decoder = new TextDecoder();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    const chunk = decoder.decode(value);
    const lines = chunk.split("\n");

    for (const line of lines) {
      if (line.startsWith("data: ")) {
        const data = JSON.parse(line.slice(6));

        // Handle streaming URL for live preview
        if (data.type === "stream" && data.streamingUrl) {
          updateAgent(agent.id, { 
            streamingUrl: data.streamingUrl,
            statusMessage: "Agent browsing..." 
          });
        }

        // Handle completion
        if (data.type === "complete") {
          updateAgent(agent.id, {
            status: data.found ? "found" : "not_found",
            statusMessage: data.found 
              ? "Manga found on this site!" 
              : "Not available on this site",
          });
        }
      }
    }
  }
};
```

### 3. cURL Example

```bash
curl -X POST "https://mino.ai/v1/automation/run-sse" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_MINO_API_KEY" \
  -d '{
    "url": "https://mangadex.org/search?q=One%20Piece",
    "goal": "You are searching for a manga/webtoon called \"One Piece\" on this website...",
    "timeout": 60000
  }'
```

---

## Goal (Prompt)

The following natural language prompt is sent to the Mino API to instruct the browser automation agent:

```
You are searching for a manga/webtoon called "${mangaTitle}" on this website.

STEP 1 - NAVIGATION:
If there's a search bar or search input, enter "${mangaTitle}" and submit the search.
If there's no search bar visible, look for a search icon or link to a search page.

STEP 2 - ANALYZE RESULTS:
Look at the search results or page content carefully.
Check if "${mangaTitle}" appears in the results (exact match or very close match).

STEP 3 - RETURN RESULT:
Return a JSON object:
{
  "found": true or false,
  "manga_title": "${mangaTitle}",
  "site_url": "current page URL",
  "match_confidence": "high" or "medium" or "low",
  "notes": "brief explanation of what you found or didn't find"
}

IMPORTANT: Only return "found": true if you see a clear match for "${mangaTitle}" in the results.
```

### Prompt Design Principles

| Principle | Application |
|-----------|-------------|
| **Structured Steps** | Breaks down the task into clear navigation → analysis → output phases |
| **Fallback Handling** | Accounts for sites without visible search bars |
| **Strict Matching** | Prevents false positives by requiring exact/close matches |
| **JSON Output** | Ensures machine-parseable response for automation |

---

## Sample Output

### SSE Stream Events

The Mino API returns Server-Sent Events (SSE) during execution. Here's the sequence:

#### Event 1: Streaming URL (Immediate)

```json
data: {
  "type": "STREAM_URL",
  "streamingUrl": "https://stream.mino.ai/session/abc123xyz"
}
```

This URL can be embedded in an iframe to show **live browser automation** in real-time.

#### Event 2: Progress Updates (During Execution)

```json
data: {
  "type": "PROGRESS",
  "step": "Entering search query...",
  "screenshot": "base64_encoded_screenshot_data"
}
```

```json
data: {
  "type": "PROGRESS", 
  "step": "Analyzing search results...",
  "screenshot": "base64_encoded_screenshot_data"
}
```

#### Event 3: Completion (Final Result)

```json
data: {
  "type": "COMPLETE",
  "resultJson": {
    "found": true,
    "manga_title": "One Piece",
    "site_url": "https://mangadex.org/title/a1c7c817-4e59-43b7-9365-09675a149a6f/one-piece",
    "match_confidence": "high",
    "notes": "Found 'One Piece' by Eiichiro Oda with 1100+ chapters available"
  },
  "duration": 12453
}
```

### Processed Client Events

The edge function transforms Mino events into simplified client events:

```json
// Live preview available
data: {"type": "stream", "streamingUrl": "https://stream.mino.ai/session/abc123xyz"}

// Search complete - manga found
data: {"type": "complete", "found": true}

// Search complete - manga not found
data: {"type": "complete", "found": false}

// Error occurred
data: {"type": "error", "error": "Search failed"}
```

---

## Error Handling

### Rate Limiting (Gemini API)

When Gemini API returns `429 Too Many Requests`, the system falls back to predefined sites:

```typescript
const defaultSites = [
  { name: "MangaDex", url: `https://mangadex.org/search?q=${encodedTitle}` },
  { name: "MangaKakalot", url: `https://mangakakalot.com/search/story/${encodedTitle}` },
  { name: "MangaReader", url: `https://mangareader.to/search?keyword=${encodedTitle}` },
  { name: "Webtoon", url: `https://www.webtoons.com/en/search?keyword=${encodedTitle}` },
  { name: "Manganato", url: `https://manganato.com/search/story/${encodedTitle}` },
  { name: "Tapas", url: `https://tapas.io/search?q=${encodedTitle}` },
];
```

### Mino API Errors

```typescript
if (data.type === "ERROR") {
  const event = `data: ${JSON.stringify({ 
    type: "error", 
    error: data.message || "Search failed" 
  })}\n\n`;
  controller.enqueue(encoder.encode(event));
}
```

---

## Environment Variables

| Variable | Purpose | Where Used |
|----------|---------|------------|
| `MINO_API_KEY` | Authenticate with Mino API | `search-manga` edge function |
| `GEMINI_API_KEY` | Authenticate with Gemini API | `discover-manga-sites` edge function |

---

## Quick Start

1. **Set up secrets** in your Supabase/Lovable Cloud project:
   - `MINO_API_KEY` - Get from [mino.ai](https://mino.ai)
   - `GEMINI_API_KEY` - Get from [Google AI Studio](https://makersuite.google.com)

2. **Deploy edge functions** (automatic in Lovable)

3. **Test the flow**:
   ```typescript
   import { useMangaSearch } from "@/hooks/useMangaSearch";
   
   const { search, agents, isSearching } = useMangaSearch();
   
   // Trigger search
   search("One Piece");
   
   // agents array updates in real-time with status and streamingUrl
   ```

---

## Architecture Diagram (Mermaid)

```mermaid
sequenceDiagram
    participant User
    participant Client as React Client
    participant Discover as discover-manga-sites
    participant Search as search-manga (x6)
    participant Gemini as Gemini API
    participant Mino as Mino API

    User->>Client: Enter "One Piece"
    Client->>Discover: POST /discover-manga-sites
    Discover->>Gemini: Generate site URLs
    Gemini-->>Discover: [MangaDex, MangaKakalot, ...]
    Discover-->>Client: { sites: [...] }
    
    par Parallel searches
        Client->>Search: POST /search-manga (MangaDex)
        Search->>Mino: run-sse (MangaDex URL)
        Mino-->>Search: SSE: streamingUrl
        Search-->>Client: SSE: {type: "stream", streamingUrl}
        Mino-->>Search: SSE: COMPLETE
        Search-->>Client: SSE: {type: "complete", found: true}
    and
        Client->>Search: POST /search-manga (MangaKakalot)
        Search->>Mino: run-sse (MangaKakalot URL)
        Mino-->>Search: SSE: streamingUrl
        Search-->>Client: SSE: {type: "stream", streamingUrl}
        Mino-->>Search: SSE: COMPLETE
        Search-->>Client: SSE: {type: "complete", found: false}
    end
    
    Client->>User: Display results with live previews
```


================================================
FILE: Manga-Availability-Finder/package.json
================================================
{
  "name": "vite_react_shadcn_ts",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "build:dev": "vite build --mode development",
    "lint": "eslint .",
    "preview": "vite preview",
    "test": "vitest run",
    "test:watch": "vitest"
  },
  "dependencies": {
    "@hookform/resolvers": "^3.10.0",
    "@radix-ui/react-accordion": "^1.2.11",
    "@radix-ui/react-alert-dialog": "^1.1.14",
    "@radix-ui/react-aspect-ratio": "^1.1.7",
    "@radix-ui/react-avatar": "^1.1.10",
    "@radix-ui/react-checkbox": "^1.3.2",
    "@radix-ui/react-collapsible": "^1.1.11",
    "@radix-ui/react-context-menu": "^2.2.15",
    "@radix-ui/react-dialog": "^1.1.14",
    "@radix-ui/react-dropdown-menu": "^2.1.15",
    "@radix-ui/react-hover-card": "^1.1.14",
    "@radix-ui/react-label": "^2.1.7",
    "@radix-ui/react-menubar": "^1.1.15",
    "@radix-ui/react-navigation-menu": "^1.2.13",
    "@radix-ui/react-popover": "^1.1.14",
    "@radix-ui/react-progress": "^1.1.7",
    "@radix-ui/react-radio-group": "^1.3.7",
    "@radix-ui/react-scroll-area": "^1.2.9",
    "@radix-ui/react-select": "^2.2.5",
    "@radix-ui/react-separator": "^1.1.7",
    "@radix-ui/react-slider": "^1.3.5",
    "@radix-ui/react-slot": "^1.2.3",
    "@radix-ui/react-switch": "^1.2.5",
    "@radix-ui/react-tabs": "^1.1.12",
    "@radix-ui/react-toast": "^1.2.14",
    "@radix-ui/react-toggle": "^1.1.9",
    "@radix-ui/react-toggle-group": "^1.1.10",
    "@radix-ui/react-tooltip": "^1.2.7",
    "@supabase/supabase-js": "^2.91.0",
    "@tanstack/react-query": "^5.83.0",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "cmdk": "^1.1.1",
    "date-fns": "^3.6.0",
    "embla-carousel-react": "^8.6.0",
    "input-otp": "^1.4.2",
    "lucide-react": "^0.462.0",
    "next-themes": "^0.3.0",
    "react": "^18.3.1",
    "react-day-picker": "^8.10.1",
    "react-dom": "^18.3.1",
    "react-hook-form": "^7.61.1",
    "react-resizable-panels": "^2.1.9",
    "react-router-dom": "^6.30.1",
    "recharts": "^2.15.4",
    "sonner": "^1.7.4",
    "tailwind-merge": "^2.6.0",
    "tailwindcss-animate": "^1.0.7",
    "vaul": "^0.9.9",
    "zod": "^3.25.76"
  },
  "devDependencies": {
    "@eslint/js": "^9.32.0",
    "@tailwindcss/typography": "^0.5.16",
    "@testing-library/jest-dom": "^6.6.0",
    "@testing-library/react": "^16.0.0",
    "@types/node": "^22.16.5",
    "@types/react": "^18.3.23",
    "@types/react-dom": "^18.3.7",
    "@vitejs/plugin-react-swc": "^3.11.0",
    "autoprefixer": "^10.4.21",
    "eslint": "^9.32.0",
    "eslint-plugin-react-hooks": "^5.2.0",
    "eslint-plugin-react-refresh": "^0.4.20",
    "globals": "^15.15.0",
    "jsdom": "^20.0.3",
    "lovable-tagger": "^1.1.13",
    "postcss": "^8.5.6",
    "tailwindcss": "^3.4.17",
    "typescript": "^5.8.3",
    "typescript-eslint": "^8.38.0",
    "vite": "^5.4.19",
    "vitest": "^3.2.4"
  }
}


================================================
FILE: Manga-Availability-Finder/public/robots.txt
================================================
User-agent: Googlebot
Allow: /

User-agent: Bingbot
Allow: /

User-agent: Twitterbot
Allow: /

User-agent: facebookexternalhit
Allow: /

User-agent: *
Allow: /


================================================
FILE: Manga-Availability-Finder/src/App.tsx
================================================
import { Toaster } from "@/components/ui/toaster";
import { Toaster as Sonner } from "@/components/ui/sonner";
import { TooltipProvider } from "@/components/ui/tooltip";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Index from "./pages/Index";
import NotFound from "./pages/NotFound";

const queryClient = new QueryClient();

const App = () => (
  <QueryClientProvider client={queryClient}>
    <TooltipProvider>
      <Toaster />
      <Sonner />
      <BrowserRouter>
        <Routes>
          <Route path="/" element={<Index />} />
          {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
          <Route path="*" element={<NotFound />} />
        </Routes>
      </BrowserRouter>
    </TooltipProvider>
  </QueryClientProvider>
);

export default App;


================================================
FILE: Manga-Availability-Finder/src/components/AgentCard.tsx
================================================
import { ExternalLink, CheckCircle2, XCircle, Loader2, Globe, Eye } from "lucide-react";
import { cn } from "@/lib/utils";

export type AgentStatus = "idle" | "searching" | "found" | "not_found" | "error";

interface AgentCardProps {
  siteName: string;
  siteUrl: string;
  status: AgentStatus;
  statusMessage?: string;
  streamingUrl?: string;
  mangaTitle: string;
}

const statusConfig: Record<AgentStatus, { icon: React.ReactNode; label: string; className: string }> = {
  idle: {
    icon: <Globe className="w-4 h-4" />,
    label: "Ready",
    className: "text-muted-foreground bg-muted/50",
  },
  searching: {
    icon: <Loader2 className="w-4 h-4 animate-spin" />,
    label: "Searching...",
    className: "status-searching",
  },
  found: {
    icon: <CheckCircle2 className="w-4 h-4" />,
    label: "Found",
    className: "status-found",
  },
  not_found: {
    icon: <XCircle className="w-4 h-4" />,
    label: "Not Found",
    className: "status-not-found",
  },
  error: {
    icon: <XCircle className="w-4 h-4" />,
    label: "Error",
    className: "status-not-found",
  },
};

export function AgentCard({
  siteName,
  siteUrl,
  status,
  statusMessage,
  streamingUrl,
  mangaTitle,
}: AgentCardProps) {
  const config = statusConfig[status];

  return (
    <div
      className={cn(
        "group relative overflow-hidden rounded-xl bg-card border border-border transition-all duration-300 hover-lift",
        status === "searching" && "animate-pulse-border border-secondary/50",
        status === "found" && "border-success/50",
        status === "not_found" && "border-destructive/30"
      )}
    >
      {/* Scanning effect for searching state */}
      {status === "searching" && (
        <div className="absolute inset-0 overflow-hidden pointer-events-none">
          <div className="absolute inset-0 bg-gradient-to-b from-transparent via-secondary/5 to-transparent animate-[scanLine_2s_linear_infinite]" />
        </div>
      )}

      {/* Header */}
      <div className="p-4 border-b border-border/50">
        <div className="flex items-center justify-between gap-3">
          <div className="flex items-center gap-3 min-w-0">
            <div className="w-10 h-10 rounded-lg bg-muted flex items-center justify-center shrink-0">
              <Globe className="w-5 h-5 text-muted-foreground" />
            </div>
            <div className="min-w-0">
              <h3 className="font-display font-semibold text-foreground truncate">
                {siteName}
              </h3>
              <p className="text-xs text-muted-foreground truncate">{new URL(siteUrl).hostname}</p>
            </div>
          </div>
          
          <div className={cn("flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium shrink-0", config.className)}>
            {config.icon}
            <span>{config.label}</span>
          </div>
        </div>
      </div>

      {/* Browser preview area */}
      <div className="relative aspect-video bg-muted/30">
        {streamingUrl && status === "searching" ? (
          <iframe
            src={streamingUrl}
            className="w-full h-full border-0"
            title={`${siteName} browser view`}
            sandbox="allow-same-origin"
          />
        ) : (
          <div className="absolute inset-0 flex flex-col items-center justify-center gap-2 text-muted-foreground">
            {status === "idle" && (
              <>
                <Eye className="w-8 h-8 opacity-50" />
                <span className="text-sm">Waiting to start...</span>
              </>
            )}
            {status === "searching" && !streamingUrl && (
              <>
                <Loader2 className="w-8 h-8 animate-spin text-secondary" />
                <span className="text-sm text-secondary">Connecting agent...</span>
              </>
            )}
            {status === "found" && (
              <div className="text-center p-4">
                <CheckCircle2 className="w-12 h-12 text-success mx-auto mb-2" />
                <p className="font-display font-semibold text-success">Found!</p>
                <p className="text-sm mt-1">"{mangaTitle}" is available</p>
              </div>
            )}
            {status === "not_found" && (
              <div className="text-center p-4">
                <XCircle className="w-12 h-12 text-destructive/70 mx-auto mb-2" />
                <p className="font-display font-semibold text-destructive/70">Not Found</p>
                <p className="text-sm mt-1">Not available on this site</p>
              </div>
            )}
            {status === "error" && (
              <div className="text-center p-4">
                <XCircle className="w-12 h-12 text-destructive/70 mx-auto mb-2" />
                <p className="font-display font-semibold text-destructive/70">Error</p>
                <p className="text-sm mt-1">Failed to search</p>
              </div>
            )}
          </div>
        )}
      </div>

      {/* Status message */}
      {statusMessage && (
        <div className="px-4 py-2 border-t border-border/50 bg-muted/20">
          <p className="text-xs text-muted-foreground truncate">{statusMessage}</p>
        </div>
      )}

      {/* Footer with link */}
      <div className="p-3 border-t border-border/50">
        <a
          href={siteUrl}
          target="_blank"
          rel="noopener noreferrer"
          className="flex items-center justify-center gap-2 text-sm text-muted-foreground hover:text-primary transition-colors"
        >
          <span>Visit Site</span>
          <ExternalLink className="w-4 h-4" />
        </a>
      </div>
    </div>
  );
}


================================================
FILE: Manga-Availability-Finder/src/components/NavLink.tsx
================================================
import { NavLink as RouterNavLink, NavLinkProps } from "react-router-dom";
import { forwardRef } from "react";
import { cn } from "@/lib/utils";

interface NavLinkCompatProps extends Omit<NavLinkProps, "className"> {
  className?: string;
  activeClassName?: string;
  pendingClassName?: string;
}

const NavLink = forwardRef<HTMLAnchorElement, NavLinkCompatProps>(
  ({ className, activeClassName, pendingClassName, to, ...props }, ref) => {
    return (
      <RouterNavLink
        ref={ref}
        to={to}
        className={({ isActive, isPending }) =>
          cn(className, isActive && activeClassName, isPending && pendingClassName)
        }
        {...props}
      />
    );
  },
);

NavLink.displayName = "NavLink";

export { NavLink };


================================================
FILE: Manga-Availability-Finder/src/components/ResultsSummary.tsx
================================================
import { CheckCircle2, XCircle, Loader2, BookOpen } from "lucide-react";
import { AgentStatus } from "./AgentCard";

interface SearchResult {
  siteName: string;
  siteUrl: string;
  status: AgentStatus;
}

interface ResultsSummaryProps {
  mangaTitle: string;
  results: SearchResult[];
  isSearching: boolean;
}

export function ResultsSummary({ mangaTitle, results, isSearching }: ResultsSummaryProps) {
  const foundCount = results.filter((r) => r.status === "found").length;
  const searchingCount = results.filter((r) => r.status === "searching").length;
  const completedCount = results.filter((r) => r.status === "found" || r.status === "not_found" || r.status === "error").length;

  if (results.length === 0) return null;

  return (
    <div className="bg-card/50 border border-border rounded-xl p-6 backdrop-blur-sm">
      {/* Header */}
      <div className="flex items-center gap-3 mb-6">
        <div className="w-12 h-12 rounded-xl bg-gradient-neon flex items-center justify-center">
          <BookOpen className="w-6 h-6 text-primary-foreground" />
        </div>
        <div>
          <h2 className="font-display text-xl font-bold text-foreground">
            Results for "{mangaTitle}"
          </h2>
          <p className="text-sm text-muted-foreground">
            {isSearching ? (
              <>Searching {searchingCount} site{searchingCount !== 1 ? "s" : ""}...</>
            ) : (
              <>Searched {results.length} site{results.length !== 1 ? "s" : ""}</>
            )}
          </p>
        </div>
      </div>

      {/* Progress bar */}
      <div className="mb-6">
        <div className="flex items-center justify-between text-sm mb-2">
          <span className="text-muted-foreground">Progress</span>
          <span className="font-medium text-foreground">{completedCount} / {results.length}</span>
        </div>
        <div className="h-2 bg-muted rounded-full overflow-hidden">
          <div
            className="h-full bg-gradient-neon transition-all duration-500 ease-out"
            style={{ width: `${(completedCount / results.length) * 100}%` }}
          />
        </div>
      </div>

      {/* Stats */}
      <div className="grid grid-cols-3 gap-4 mb-6">
        <div className="text-center p-3 rounded-lg bg-success/10 border border-success/20">
          <div className="flex items-center justify-center gap-1 text-success mb-1">
            <CheckCircle2 className="w-4 h-4" />
            <span className="font-display font-bold text-xl">{foundCount}</span>
          </div>
          <p className="text-xs text-muted-foreground">Found</p>
        </div>
        <div className="text-center p-3 rounded-lg bg-secondary/10 border border-secondary/20">
          <div className="flex items-center justify-center gap-1 text-secondary mb-1">
            <Loader2 className={`w-4 h-4 ${searchingCount > 0 ? "animate-spin" : ""}`} />
            <span className="font-display font-bold text-xl">{searchingCount}</span>
          </div>
          <p className="text-xs text-muted-foreground">Searching</p>
        </div>
        <div className="text-center p-3 rounded-lg bg-muted/50 border border-border">
          <div className="flex items-center justify-center gap-1 text-muted-foreground mb-1">
            <XCircle className="w-4 h-4" />
            <span className="font-display font-bold text-xl">
              {results.filter((r) => r.status === "not_found").length}
            </span>
          </div>
          <p className="text-xs text-muted-foreground">Not Found</p>
        </div>
      </div>

      {/* Results list */}
      <div className="space-y-2 max-h-64 overflow-y-auto scrollbar-cyber">
        {results.map((result) => (
          <div
            key={result.siteUrl}
            className="flex items-center justify-between p-3 rounded-lg bg-muted/30 border border-border/50"
          >
            <div className="flex items-center gap-3 min-w-0">
              {result.status === "found" && (
                <CheckCircle2 className="w-5 h-5 text-success shrink-0" />
              )}
              {result.status === "not_found" && (
                <XCircle className="w-5 h-5 text-muted-foreground shrink-0" />
              )}
              {result.status === "searching" && (
                <Loader2 className="w-5 h-5 text-secondary animate-spin shrink-0" />
              )}
              {result.status === "error" && (
                <XCircle className="w-5 h-5 text-destructive shrink-0" />
              )}
              {result.status === "idle" && (
                <div className="w-5 h-5 rounded-full border-2 border-muted-foreground shrink-0" />
              )}
              <span className="text-sm font-medium truncate">{result.siteName}</span>
            </div>
            {result.status === "found" && (
              <a
                href={result.siteUrl}
                target="_blank"
                rel="noopener noreferrer"
                className="text-xs text-primary hover:underline shrink-0"
              >
                Read Now →
              </a>
            )}
          </div>
        ))}
      </div>
    </div>
  );
}


================================================
FILE: Manga-Availability-Finder/src/components/SearchHero.tsx
================================================
import { useState } from "react";
import { Search, Sparkles } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";

interface SearchHeroProps {
  onSearch: (query: string) => void;
  isSearching: boolean;
}

export function SearchHero({ onSearch, isSearching }: SearchHeroProps) {
  const [query, setQuery] = useState("");

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (query.trim()) {
      onSearch(query.trim());
    }
  };

  return (
    <div className="relative min-h-[50vh] flex flex-col items-center justify-center px-4 py-16">
      {/* Background glow effects */}
      <div className="absolute inset-0 overflow-hidden pointer-events-none">
        <div className="absolute top-1/4 left-1/4 w-96 h-96 bg-primary/10 rounded-full blur-[120px]" />
        <div className="absolute bottom-1/4 right-1/4 w-80 h-80 bg-secondary/10 rounded-full blur-[100px]" />
        <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] bg-accent/5 rounded-full blur-[150px]" />
      </div>

      {/* Content */}
      <div className="relative z-10 max-w-3xl w-full text-center space-y-8">
        {/* Title */}
        <div className="space-y-4">
          <div className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-muted/50 border border-border text-sm text-muted-foreground backdrop-blur-sm">
            <Sparkles className="w-4 h-4 text-primary" />
            <span>AI-Powered Manga Discovery</span>
          </div>
          
          <h1 className="font-display text-4xl md:text-6xl lg:text-7xl font-bold tracking-tight">
            <span className="text-foreground">Manga</span>
            <span className="gradient-text">Finder</span>
          </h1>
          
          <p className="text-lg md:text-xl text-muted-foreground max-w-xl mx-auto">
            Search across multiple manga & webtoon sites simultaneously using AI-powered web agents
          </p>
        </div>

        {/* Search Form */}
        <form onSubmit={handleSubmit} className="relative">
          <div className="flex flex-col sm:flex-row gap-3 max-w-2xl mx-auto">
            <div className="relative flex-1">
              <Search className="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-muted-foreground" />
              <Input
                type="text"
                placeholder="Enter manga or webtoon title..."
                value={query}
                onChange={(e) => setQuery(e.target.value)}
                className="pl-12 h-14 text-lg bg-card/50 border-border/50 backdrop-blur-sm focus:bg-card"
                disabled={isSearching}
              />
            </div>
            <Button
              type="submit"
              variant="cyber"
              size="xl"
              disabled={isSearching || !query.trim()}
              className="min-w-[140px]"
            >
              {isSearching ? (
                <>
                  <span className="animate-pulse">Searching</span>
                  <span className="flex gap-0.5">
                    <span className="animate-bounce" style={{ animationDelay: "0ms" }}>.</span>
                    <span className="animate-bounce" style={{ animationDelay: "150ms" }}>.</span>
                    <span className="animate-bounce" style={{ animationDelay: "300ms" }}>.</span>
                  </span>
                </>
              ) : (
                <>
                  <Search className="w-5 h-5" />
                  Search
                </>
              )}
            </Button>
          </div>
        </form>

        {/* Example searches */}
        <div className="flex flex-wrap justify-center gap-2">
          <span className="text-sm text-muted-foreground">Try:</span>
          {["One Piece", "Solo Leveling", "Chainsaw Man", "Tower of God"].map((example) => (
            <button
              key={example}
              onClick={() => {
                setQuery(example);
                onSearch(example);
              }}
              disabled={isSearching}
              className="px-3 py-1 text-sm rounded-full bg-muted/50 border border-border hover:border-primary/50 hover:bg-muted transition-all duration-200 disabled:opacity-50"
            >
              {example}
            </button>
          ))}
        </div>
      </div>
    </div>
  );
}


================================================
FILE: Manga-Availability-Finder/src/components/ui/avatar.tsx
================================================
import * as React from "react";
import * as AvatarPrimitive from "@radix-ui/react-avatar";

import { cn } from "@/lib/utils";

const Avatar = React.forwardRef<
  React.ElementRef<typeof AvatarPrimitive.Root>,
  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
>(({ className, ...props }, ref) => (
  <AvatarPrimitive.Root
    ref={ref}
    className={cn("relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", className)}
    {...props}
  />
));
Avatar.displayName = AvatarPrimitive.Root.displayName;

const AvatarImage = React.forwardRef<
  React.ElementRef<typeof AvatarPrimitive.Image>,
  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
>(({ className, ...props }, ref) => (
  <AvatarPrimitive.Image ref={ref} className={cn("aspect-square h-full w-full", className)} {...props} />
));
AvatarImage.displayName = AvatarPrimitive.Image.displayName;

const AvatarFallback = React.forwardRef<
  React.ElementRef<typeof AvatarPrimitive.Fallback>,
  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
>(({ className, ...props }, ref) => (
  <AvatarPrimitive.Fallback
    ref={ref}
    className={cn("flex h-full w-full items-center justify-center rounded-full bg-muted", className)}
    {...props}
  />
));
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;

export { Avatar, AvatarImage, AvatarFallback };


================================================
FILE: Manga-Availability-Finder/src/components/ui/button.tsx
================================================
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";

import { cn } from "@/lib/utils";

const buttonVariants = cva(
  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg text-sm font-medium ring-offset-background transition-all duration-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground hover:bg-primary/90 shadow-lg hover:shadow-primary/30",
        destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
        outline: "border border-border bg-transparent hover:bg-muted hover:text-foreground",
        secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
        ghost: "hover:bg-muted hover:text-foreground",
        link: "text-primary underline-offset-4 hover:underline",
        neon: "relative bg-transparent border-2 border-primary text-primary hover:bg-primary/10 hover:shadow-[0_0_20px_hsl(var(--primary)/0.5)] transition-all duration-300",
        cyber: "bg-gradient-to-r from-primary via-accent to-secondary text-primary-foreground font-semibold uppercase tracking-wider hover:opacity-90 shadow-[0_0_30px_hsl(var(--primary)/0.4)] hover:shadow-[0_0_40px_hsl(var(--primary)/0.6)]",
        glow: "bg-card border border-primary/50 text-foreground hover:border-primary hover:shadow-[0_0_20px_hsl(var(--primary)/0.4)]",
      },
      size: {
        default: "h-10 px-4 py-2",
        sm: "h-9 rounded-md px-3",
        lg: "h-12 rounded-lg px-8 text-base",
        xl: "h-14 rounded-xl px-10 text-lg",
        icon: "h-10 w-10",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  },
);

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  asChild?: boolean;
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, asChild = false, ...props }, ref) => {
    const Comp = asChild ? Slot : "button";
    return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />;
  },
);
Button.displayName = "Button";

export { Button, buttonVariants };


================================================
FILE: Manga-Availability-Finder/src/components/ui/card.tsx
================================================
import * as React from "react";

import { cn } from "@/lib/utils";

const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
  <div ref={ref} className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)} {...props} />
));
Card.displayName = "Card";

const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
  ({ className, ...props }, ref) => (
    <div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
  ),
);
CardHeader.displayName = "CardHeader";

const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
  ({ className, ...props }, ref) => (
    <h3 ref={ref} className={cn("text-2xl font-semibold leading-none tracking-tight", className)} {...props} />
  ),
);
CardTitle.displayName = "CardTitle";

const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
  ({ className, ...props }, ref) => (
    <p ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
  ),
);
CardDescription.displayName = "CardDescription";

const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
  ({ className, ...props }, ref) => <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />,
);
CardContent.displayName = "CardContent";

const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
  ({ className, ...props }, ref) => (
    <div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
  ),
);
CardFooter.displayName = "CardFooter";

export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };


================================================
FILE: Manga-Availability-Finder/src/hooks/useMangaSearch.ts
================================================
import { useState, useCallback } from "react";
import { supabase } from "@/integrations/supabase/client";
import { AgentStatus } from "@/components/AgentCard";

export interface SiteAgent {
  id: string;
  siteName: string;
  siteUrl: string;
  status: AgentStatus;
  statusMessage?: string;
  streamingUrl?: string;
}

export function useMangaSearch() {
  const [isSearching, setIsSearching] = useState(false);
  const [agents, setAgents] = useState<SiteAgent[]>([]);
  const [mangaTitle, setMangaTitle] = useState("");

  const updateAgent = useCallback((id: string, updates: Partial<SiteAgent>) => {
    setAgents((prev) =>
      prev.map((agent) => (agent.id === id ? { ...agent, ...updates } : agent))
    );
  }, []);

  const searchSite = useCallback(
    async (agent: SiteAgent, title: string) => {
      updateAgent(agent.id, {
        status: "searching",
        statusMessage: "Connecting to agent...",
      });

      try {
        const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
        const supabaseKey = import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY;

        const response = await fetch(
          `${supabaseUrl}/functions/v1/search-manga`,
          {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
              Authorization: `Bearer ${supabaseKey}`,
              apikey: supabaseKey,
            },
            body: JSON.stringify({ url: agent.siteUrl, mangaTitle: title }),
          }
        );

        if (!response.ok) {
          throw new Error(`HTTP error: ${response.status}`);
        }

        const contentType = response.headers.get("content-type");

        if (contentType?.includes("text/event-stream")) {
          const reader = response.body?.getReader();
          if (!reader) throw new Error("No response body");

          const decoder = new TextDecoder();

          let buffer = ""; 
          let receivedTerminalEvent = 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) {
              if (!line.startsWith("data: ")) continue;

              try {
                const data = JSON.parse(line.slice(6));

                if (data.type === "stream" && data.streamingUrl) {
                  updateAgent(agent.id, {
                    streamingUrl: data.streamingUrl,
                    statusMessage: "Agent browsing...",
                  });
                }

                if (data.type === "complete") {
                  receivedTerminalEvent = true; 
                  updateAgent(agent.id, {
                    status: data.found ? "found" : "not_found",
                    statusMessage: data.found
                      ? "Manga found on this site!"
                      : "Not available on this site",
                    streamingUrl: undefined,
                  });
                }

                if (data.type === "error") {
                  receivedTerminalEvent = true; 
                  updateAgent(agent.id, {
                    status: "error",
                    statusMessage: data.error || "Search failed",
                    streamingUrl: undefined,
                  });
                }
              } catch {
                // Ignore parse errors (partial JSON handled by buffering)
              }
            }
          }

          if (!receivedTerminalEvent) {
            updateAgent(agent.id, {
              status: "error",
              statusMessage: "Stream ended without completion signal",
              streamingUrl: undefined,
            });
          }
        } else {
          const data = await response.json();

          if (data?.found !== undefined) {
            updateAgent(agent.id, {
              status: data.found ? "found" : "not_found",
              statusMessage: data.found
                ? "Manga found on this site!"
                : "Not available on this site",
            });
          } else if (data?.error) {
            updateAgent(agent.id, {
              status: "error",
              statusMessage: data.error,
            });
          }
        }
      } catch (error) {
        console.error(`Error searching ${agent.siteName}:`, error);
        updateAgent(agent.id, {
          status: "error",
          statusMessage:
            error instanceof Error ? error.message : "Search failed",
          streamingUrl: undefined,
        });
      }
    },
    [updateAgent]
  );

  const search = useCallback(
    async (title: string) => {
      setIsSearching(true);
      setMangaTitle(title);
      setAgents([]);

      try {
        const { data: urlsData, error: urlsError } =
          await supabase.functions.invoke("discover-manga-sites", {
            body: { mangaTitle: title },
          });

        if (urlsError) {
          throw new Error(urlsError.message);
        }

        const sites: Array<{ name: string; url: string }> =
          urlsData?.sites || [];

        if (sites.length === 0) {
          setIsSearching(false);
          return;
        }

        const initialAgents: SiteAgent[] = sites.map((site, index) => ({
          id: `agent-${index}`,
          siteName: site.name,
          siteUrl: site.url,
          status: "idle" as AgentStatus,
        }));

        setAgents(initialAgents);

        await Promise.all(
          initialAgents.map((agent) => searchSite(agent, title))
        );
      } catch (error) {
        console.error("Search error:", error);
      } finally {
        setIsSearching(false);
      }
    },
    [searchSite]
  );

  return {
    isSearching,
    agents,
    mangaTitle,
    search,
  };
}


================================================
FILE: Manga-Availability-Finder/src/integrations/supabase/client.ts
================================================
// This file is automatically generated. Do not edit it directly.
import { createClient } from '@supabase/supabase-js';
import type { Database } from './types';

const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL;
const SUPABASE_PUBLISHABLE_KEY = import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY;

// Import the supabase client like this:
// import { supabase } from "@/integrations/supabase/client";

export const supabase = createClient<Database>(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY, {
  auth: {
    storage: localStorage,
    persistSession: true,
    autoRefreshToken: true,
  }
});


================================================
FILE: Manga-Availability-Finder/src/integrations/supabase/types.ts
================================================
export type Json =
  | string
  | number
  | boolean
  | null
  | { [key: string]: Json | undefined }
  | Json[]

export type Database = {
  // Allows to automatically instantiate createClient with right options
  // instead of createClient<Database, { PostgrestVersion: 'XX' }>(URL, KEY)
  __InternalSupabase: {
    PostgrestVersion: "14.1"
  }
  public: {
    Tables: {
      [_ in never]: never
    }
    Views: {
      [_ in never]: never
    }
    Functions: {
      [_ in never]: never
    }
    Enums: {
      [_ in never]: never
    }
    CompositeTypes: {
      [_ in never]: never
    }
  }
}

type DatabaseWithoutInternals = Omit<Database, "__InternalSupabase">

type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, "public">]

export type Tables<
  DefaultSchemaTableNameOrOptions extends
    | keyof (DefaultSchema["Tables"] & DefaultSchema["Views"])
    | { schema: keyof DatabaseWithoutInternals },
  TableName extends DefaultSchemaTableNameOrOptions extends {
    schema: keyof DatabaseWithoutInternals
  }
    ? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] &
        DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])
    : never = never,
> = DefaultSchemaTableNameOrOptions extends {
  schema: keyof DatabaseWithoutInternals
}
  ? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] &
      DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends {
      Row: infer R
    }
    ? R
    : never
  : DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] &
        DefaultSchema["Views"])
    ? (DefaultSchema["Tables"] &
        DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends {
        Row: infer R
      }
      ? R
      : never
    : never

export type TablesInsert<
  DefaultSchemaTableNameOrOptions extends
    | keyof DefaultSchema["Tables"]
    | { schema: keyof DatabaseWithoutInternals },
  TableName extends DefaultSchemaTableNameOrOptions extends {
    schema: keyof DatabaseWithoutInternals
  }
    ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"]
    : never = never,
> = DefaultSchemaTableNameOrOptions extends {
  schema: keyof DatabaseWithoutInternals
}
  ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends {
      Insert: infer I
    }
    ? I
    : never
  : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"]
    ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends {
        Insert: infer I
      }
      ? I
      : never
    : never

export type TablesUpdate<
  DefaultSchemaTableNameOrOptions extends
    | keyof DefaultSchema["Tables"]
    | { schema: keyof DatabaseWithoutInternals },
  TableName extends DefaultSchemaTableNameOrOptions extends {
    schema: keyof DatabaseWithoutInternals
  }
    ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"]
    : never = never,
> = DefaultSchemaTableNameOrOptions extends {
  schema: keyof DatabaseWithoutInternals
}
  ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends {
      Update: infer U
    }
    ? U
    : never
  : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"]
    ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends {
        Update: infer U
      }
      ? U
      : never
    : never

export type Enums<
  DefaultSchemaEnumNameOrOptions extends
    | keyof DefaultSchema["Enums"]
    | { schema: keyof DatabaseWithoutInternals },
  EnumName extends DefaultSchemaEnumNameOrOptions extends {
    schema: keyof DatabaseWithoutInternals
  }
    ? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"]
    : never = never,
> = DefaultSchemaEnumNameOrOptions extends {
  schema: keyof DatabaseWithoutInternals
}
  ? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName]
  : DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"]
    ? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions]
    : never

export type CompositeTypes<
  PublicCompositeTypeNameOrOptions extends
    | keyof DefaultSchema["CompositeTypes"]
    | { schema: keyof DatabaseWithoutInternals },
  CompositeTypeName extends PublicCompositeTypeNameOrOptions extends {
    schema: keyof DatabaseWithoutInternals
  }
    ? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"]
    : never = never,
> = PublicCompositeTypeNameOrOptions extends {
  schema: keyof DatabaseWithoutInternals
}
  ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName]
  : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"]
    ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions]
    : never

export const Constants = {
  public: {
    Enums: {},
  },
} as const


================================================
FILE: Manga-Availability-Finder/src/lib/utils.ts
================================================
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}


================================================
FILE: Manga-Availability-Finder/src/pages/Index.tsx
================================================
import { SearchHero } from "@/components/SearchHero";
import { AgentCard } from "@/components/AgentCard";
import { ResultsSummary } from "@/components/ResultsSummary";
import { useMangaSearch } from "@/hooks/useMangaSearch";

const Index = () => {
  const { isSearching, agents, mangaTitle, search } = useMangaSearch();

  return (
    <div className="min-h-screen bg-background cyber-grid">
      {/* Hero Section */}
      <SearchHero onSearch={search} isSearching={isSearching} />

      {/* Results Section */}
      {agents.length > 0 && (
        <div className="container mx-auto px-4 pb-16">
          <div className="grid lg:grid-cols-[1fr_350px] gap-8">
            {/* Agent Cards Grid */}
            <div className="space-y-6">
              <div className="flex items-center justify-between">
                <h2 className="font-display text-2xl font-bold text-foreground">
                  Search Agents
                </h2>
                <span className="text-sm text-muted-foreground">
                  {agents.length} site{agents.length !== 1 ? "s" : ""} being searched
                </span>
              </div>

              <div className="grid sm:grid-cols-2 xl:grid-cols-3 gap-4">
                {agents.map((agent, index) => (
                  <div
                    key={agent.id}
                    className="animate-fade-in"
                    style={{ animationDelay: `${index * 100}ms` }}
                  >
                    <AgentCard
                      siteName={agent.siteName}
                      siteUrl={agent.siteUrl}
                      status={agent.status}
                      statusMessage={agent.statusMessage}
                      streamingUrl={agent.streamingUrl}
                      mangaTitle={mangaTitle}
                    />
                  </div>
                ))}
              </div>
            </div>

            {/* Results Summary Sidebar */}
            <div className="lg:sticky lg:top-8 h-fit">
              <ResultsSummary
                mangaTitle={mangaTitle}
                results={agents.map((a) => ({
                  siteName: a.siteName,
                  siteUrl: a.siteUrl,
                  status: a.status,
                }))}
                isSearching={isSearching}
              />
            </div>
          </div>
        </div>
      )}

      {/* Empty state hint */}
      {agents.length === 0 && !isSearching && (
        <div className="container mx-auto px-4 pb-16 text-center">
          <div className="max-w-md mx-auto p-8 rounded-2xl bg-card/30 border border-border/50 backdrop-blur-sm">
            <div className="w-16 h-16 mx-auto mb-4 rounded-2xl bg-gradient-neon flex items-center justify-center">
              <span className="text-3xl">📚</span>
            </div>
            <h3 className="font-display text-xl font-semibold mb-2">Ready to Search</h3>
            <p className="text-muted-foreground">
              Enter a manga or webtoon title above to search across multiple sites simultaneously
            </p>
          </div>
        </div>
      )}
    </div>
  );
};

export default Index;


================================================
FILE: Manga-Availability-Finder/src/pages/NotFound.tsx
================================================
import { useLocation } from "react-router-dom";
import { useEffect } from "react";

const NotFound = () => {
  const location = useLocation();

  useEffect(() => {
    console.error("404 Error: User attempted to access non-existent route:", location.pathname);
  }, [location.pathname]);

  return (
    <div className="flex min-h-screen items-center justify-center bg-muted">
      <div className="text-center">
        <h1 className="mb-4 text-4xl font-bold">404</h1>
        <p className="mb-4 text-xl text-muted-foreground">Oops! Page not found</p>
        <a href="/" className="text-primary underline hover:text-primary/90">
          Return to Home
        </a>
      </div>
    </div>
  );
};

export default NotFound;


================================================
FILE: Manga-Availability-Finder/supabase/config.toml
================================================
project_id = "vodjiazkxpszllbonlck"

[functions.discover-manga-sites]
verify_jwt = false

[functions.search-manga]
verify_jwt = false


================================================
FILE: Manga-Availability-Finder/supabase/functions/discover-manga-sites/index.ts
================================================
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";

const corsHeaders = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
};

serve(async (req) => {
  if (req.method === "OPTIONS") {
    return new Response(null, { headers: corsHeaders });
  }

  try {
    const { mangaTitle } = await req.json();

    if (!mangaTitle) {
      return new Response(
        JSON.stringify({ error: "mangaTitle is required" }),
        { status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } }
      );
    }

    const GEMINI_API_KEY = Deno.env.get("GEMINI_API_KEY");
    if (!GEMINI_API_KEY) {
      throw new Error("GEMINI_API_KEY is not configured");
    }

    const prompt = `You are a manga/webtoon site discovery assistant. Given a manga or webtoon title, return a JSON array of 5-6 popular manga/webtoon reading websites where users can potentially find and read this title.

For the manga/webtoon: "${mangaTitle}"

Return ONLY a valid JSON object with this exact structure (no markdown, no code blocks):
{
  "sites": [
    {"name": "Site Name", "url": "https://example.com/search?q=${encodeURIComponent(mangaTitle)}"},
    ...
  ]
}

Include sites like:
- MangaDex (mangadex.org)
- MangaKakalot (mangakakalot.com)  
- MangaReader (mangareader.to)
- Webtoon (webtoons.com)
- Tapas (tapas.io)
- Manganato (manganato.com)

Make sure the URLs include a search query for the manga title where possible. Return exactly 5-6 sites.`;

    const response = await fetch(
  "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-goog-api-key": GEMINI_API_KEY,
    },
    body: JSON.stringify({
      contents: [{ parts: [{ text: prompt }] }],
      generationConfig: {
        temperature: 0.3,
        maxOutputTokens: 1024,
      },
    }),
  }
);


    // Default sites to use as fallback
    const defaultSites = [
      { name: "MangaDex", url: `https://mangadex.org/search?q=${encodeURIComponent(mangaTitle)}` },
      { name: "MangaKakalot", url: `https://mangakakalot.com/search/story/${encodeURIComponent(mangaTitle.toLowerCase().replace(/\s+/g, '_'))}` },
      { name: "MangaReader", url: `https://mangareader.to/search?keyword=${encodeURIComponent(mangaTitle)}` },
      { name: "Webtoon", url: `https://www.webtoons.com/en/search?keyword=${encodeURIComponent(mangaTitle)}` },
      { name: "Manganato", url: `https://manganato.com/search/story/${encodeURIComponent(mangaTitle.toLowerCase().replace(/\s+/g, '_'))}` },
      { name: "Tapas", url: `https://tapas.io/search?q=${encodeURIComponent(mangaTitle)}` },
    ];

    if (!response.ok) {
      const errorText = await response.text();
      console.error("Gemini API error:", response.status, errorText);
      
      // If rate limited (429) or other errors, use fallback sites instead of failing
      if (response.status === 429 || response.status >= 500) {
        console.log("Using fallback sites due to API error");
        return new Response(
          JSON.stringify({ sites: defaultSites }),
          { headers: { ...corsHeaders, "Content-Type": "application/json" } }
        );
      }
      
      throw new Error(`Gemini API error: ${response.status}`);
    }

    const data = await response.json();
    const textContent = data.candidates?.[0]?.content?.parts?.[0]?.text || "";

    // Parse the JSON response
    let sites: Array<{ name: string; url: string }> = [];
    try {
      // Try to extract JSON from the response
      const jsonMatch = textContent.match(/\{[\s\S]*\}/);
      if (jsonMatch) {
        const parsed = JSON.parse(jsonMatch[0]);
        sites = parsed.sites || [];
      }
    } catch (parseError) {
      console.error("Failed to parse Gemini response:", textContent);
      sites = defaultSites;
    }
    
    // If no sites found, use defaults
    if (sites.length === 0) {
      sites = defaultSites;
    }

    return new Response(
      JSON.stringify({ sites }),
      { headers: { ...corsHeaders, "Content-Type": "application/json" } }
    );
  } catch (error) {
    console.error("Error in discover-manga-sites:", error);
    return new Response(
      JSON.stringify({ error: error instanceof Error ? error.message : "Unknown error" }),
      { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }
    );
  }
});


================================================
FILE: Manga-Availability-Finder/supabase/functions/search-manga/index.ts
================================================
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";

const corsHeaders = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
};

serve(async (req) => {
  if (req.method === "OPTIONS") {
    return new Response(null, { headers: corsHeaders });
  }

  try {
    const { url, mangaTitle } = await req.json();

    if (!url || !mangaTitle) {
      return new Response(
        JSON.stringify({ error: "url and mangaTitle are required" }),
        { status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } }
      );
    }

    const MINO_API_KEY = Deno.env.get("MINO_API_KEY");
    if (!MINO_API_KEY) {
      throw new Error("MINO_API_KEY is not configured");
    }

    const goal = `You are searching for a manga/webtoon called "${mangaTitle}" on this website.

STEP 1 - NAVIGATION:
If there's a search bar or search input, enter "${mangaTitle}" and submit the search.
If there's no search bar visible, look for a search icon or link to a search page.

STEP 2 - ANALYZE RESULTS:
Look at the search results or page content carefully.
Check if "${mangaTitle}" appears in the results (exact match or very close match).

STEP 3 - RETURN RESULT:
Return a JSON object:
{
  "found": true or false,
  "manga_title": "${mangaTitle}",
  "site_url": "current page URL",
  "match_confidence": "high" or "medium" or "low",
  "notes": "brief explanation of what you found or didn't find"
}

IMPORTANT: Only return "found": true if you see a clear match for "${mangaTitle}" in the results.`;

    const response = await fetch("https://mino.ai/v1/automation/run-sse", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-API-Key": MINO_API_KEY,
      },
      body: JSON.stringify({
        url,
        goal,
        timeout: 60000,
      }),
    });

    if (!response.ok) {
      const errorText = await response.text();
      console.error("Mino API error:", response.status, errorText);
      throw new Error(`Mino API error: ${response.status}`);
    }

    // Stream SSE events back to client
    const sseHeaders = {
      ...corsHeaders,
      "Content-Type": "text/event-stream",
      "Cache-Control": "no-cache",
      "Connection": "keep-alive",
    };

    const reader = response.body?.getReader();
    if (!reader) {
      throw new Error("No response body");
    }

    const stream = new ReadableStream({
      async start(controller) {
        const decoder = new TextDecoder();
        const encoder = new TextEncoder();
        let streamingUrlSent = false;

        let buffer = "";

        try {
          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: ")) {
                try {
                  const data = JSON.parse(line.slice(6));

                  // Send streaming URL immediately when available
                  if (data.streamingUrl && !streamingUrlSent) {
                    streamingUrlSent = true;
                    const event = `data: ${JSON.stringify({ type: "stream", streamingUrl: data.streamingUrl })}\n\n`;
                    controller.enqueue(encoder.encode(event));
                  }

                  // Check for completion
                  if (data.type === "COMPLETE" && data.resultJson) {
                    let found = false;
                    try {
                      const resultData = typeof data.resultJson === 'string' 
                        ? JSON.parse(data.resultJson) 
                        : data.resultJson;
                      found = resultData.found === true;
                    } catch {
                      const resultStr = JSON.stringify(data.resultJson).toLowerCase();
                      found = resultStr.includes('"found": true') || resultStr.includes('"found":true');
                    }
                    const event = `data: ${JSON.stringify({ type: "complete", found })}\n\n`;
                    controller.enqueue(encoder.encode(event));
                  }

                  // Handle errors
                  if (data.type === "ERROR") {
                    const event = `data: ${JSON.stringify({ type: "error", error: data.message || "Search failed" })}\n\n`;
                    controller.enqueue(encoder.encode(event));
                  }
                } catch {
                  // Ignore parse errors
                }
              }
            }
          }
        } catch (error) {
          const event = `data: ${JSON.stringify({ type: "error", error: "Stream error" })}\n\n`;
          controller.enqueue(encoder.encode(event));
        } finally {
          controller.close();
        }
      },
    });

    return new Response(stream, { headers: sseHeaders });
  } catch (error) {
    console.error("Error in search-manga:", error);
    return new Response(
      JSON.stringify({ 
        error: error instanceof Error ? error.message : "Unknown error",
        found: false 
      }),
      { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }
    );
  }
});


================================================
FILE: Manga-Availability-Finder/tsconfig.json
================================================
{
  "files": [],
  "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }],
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    },
    "noImplicitAny": false,
    "noUnusedParameters": false,
    "skipLibCheck": true,
    "allowJs": true,
    "noUnusedLocals": false,
    "strictNullChecks": false
  }
}


================================================
FILE: N8N_WorkFlows/Competitor Scout CLI/Competitor Scout via Tinyfish.json
================================================
{
  "name": "Competitor Scout via Tinyfish (Form)",
  "nodes": [
    {
      "parameters": {
        "url": "={{ 'https://agent.tinyfish.ai/v1/runs/' + encodeURIComponent($json.run_id) }}",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "tinyfishApi",
        "options": {}
      },
      "id": "4b3b34dc-42cd-452d-9822-b311805181bb",
      "name": "Get Tinyfish Status",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        -992,
        960
      ],
      "credentials": {
        "tinyfishApi": {
          "id": "fGW1Y2sIYgdmiJHf",
          "name": "TinyFish Web Agent account"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "id-1",
              "leftValue": "={{ $json.allDone }}",
              "rightValue": "true",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "or"
        },
        "options": {}
      },
      "id": "737449b9-dca0-4fcd-b567-6d3688b6b145",
      "name": "Check If Complete",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        -544,
        880
      ]
    },
    {
      "parameters": {
        "amount": 3
      },
      "id": "b61e99af-c396-4b54-bf29-bc32ad275a69",
      "name": "Wait 3 Seconds",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        -96,
        1120
      ],
      "webhookId": "053ce8b5-6b4d-4292-bed4-eff9c30d8ff7"
    },
    {
      "parameters": {
        "jsCode": "// Evaluate Runs (aggregate across ALL incoming items)\nconst items = $input.all().map(i => i.json);\n\n// Flatten possible shapes into runs[]\n// Supports:\n// 1) Each item is a run object (your current case)\n// 2) One item is an array of runs\n// 3) One item has { data: [...] } or { runs: [...] }\nlet runs = [];\nfor (const x of items) {\n  if (Array.isArray(x)) {\n    runs.push(...x);\n  } else if (Array.isArray(x?.data)) {\n    runs.push(...x.data);\n  } else if (Array.isArray(x?.runs)) {\n    runs.push(...x.runs);\n  } else if (x && (x.run_id || x.status)) {\n    runs.push(x);\n  }\n}\n\n// De-dupe by run_id (helps if polling returns duplicates)\nconst seen = new Set();\nruns = runs.filter(r => {\n  const id = r?.run_id ?? r?.runId ?? JSON.stringify(r);\n  if (seen.has(id)) return false;\n  seen.add(id);\n  return true;\n});\n\nconst terminal = new Set([\"COMPLETED\",\"FAILED\",\"CANCELLED\"]);\nconst allDone =\n  runs.length > 0 &&\n  runs.every(r => terminal.has(String(r.status || \"\").toUpperCase()));\n\n// Preserve full raw input (all items) so nothing is lost\nreturn [{\n  json: {\n    raw: items,   // full original input across all items\n    runs,         // normalized array of runs\n    allDone\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -768,
        880
      ],
      "id": "48b7ff1f-bd98-409f-87ab-06f2796c5215",
      "name": "Evaluate Runs"
    },
    {
      "parameters": {
        "jsCode": "// Rehydrate per-run items from aggregated Check-If-Complete payload\n// Input is usually ONE item with shape: { runs: [...], raw: [...], allDone: bool }\n\nconst inItem = $input.first().json;\n\n// Prefer runs, fall back to raw, then empty\nconst arr = Array.isArray(inItem.runs)\n  ? inItem.runs\n  : (Array.isArray(inItem.raw) ? inItem.raw : []);\n\n// Extract run IDs\nconst runIds = arr\n  .map(r => r?.run_id || r?.runId)\n  .filter(Boolean);\n\n// Optionally preserve aggregated context for later nodes\n// (useful if you want to carry question/competitors forward)\nconst ctx = {\n  // keep these if they exist upstream\n  question: inItem.question,\n  competitors: inItem.competitors,\n  byName: inItem.byName,\n};\n\nreturn runIds.map(runId => ({\n  json: {\n    ...ctx,\n    runId,\n    run_id: runId\n  }\n}));"
      },
      "id": "03078890-813e-4a81-b1ec-44c7bec6781d",
      "name": "Rehydrate RunIds for Next Poll",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -320,
        1056
      ]
    },
    {
      "parameters": {
        "formTitle": "Competitive Research",
        "formDescription": "Enter your competitors and research question to analyze their websites",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Competitors (one per line)",
              "fieldType": "textarea",
              "fieldName": "competitors",
              "placeholder": "Enter each competitor as \"Name — URL\". Example:\nNotion — https://www.notion.com\nEvernote — https://evernote.com"
            },
            {
              "fieldLabel": "What do you want to know about these competitors?",
              "fieldName": "question",
              "placeholder": "Example: What sign-in methods do my competitors support?"
            }
          ]
        },
        "options": {
          "appendAttribution": false
        }
      },
      "id": "7119e58b-6a46-4012-964f-02441529c8ce",
      "name": "Competitor Research Form1",
      "type": "n8n-nodes-base.formTrigger",
      "typeVersion": 2.5,
      "position": [
        -2912,
        960
      ],
      "webhookId": "21ff05c4-69cc-422e-ae98-2d2f4e327d34"
    },
    {
      "parameters": {
        "jsCode": "// Parse competitors from form input\nconst formData = $input.first().json;\nconst competitorsText = (formData.competitors || '').trim();\nconst question = (formData.question || '').trim();\n\nfunction normalizeUrl(raw) {\n  if (!raw) return '';\n  let u = raw.trim();\n\n  // Remove trailing punctuation that often gets pasted\n  u = u.replace(/[),.;]+$/, '');\n\n  // If user pasted just domain or www., add scheme\n  if (!/^https?:\\/\\//i.test(u)) {\n    u = 'https://' + u;\n  }\n\n  // Basic sanity: if it's still not a domain-like string, return empty\n  // (optional, but helps avoid garbage)\n  const hostish = u.replace(/^https?:\\/\\//i, '');\n  if (!hostish.includes('.')) return '';\n\n  return u;\n}\n\nfunction splitNameAndUrl(line) {\n  // Remove bullets like \"- \" \"* \" \"• \"\n  const clean = line.replace(/^\\s*[-*•]\\s*/, '').trim();\n\n  // Prefer em dash delimiter\n  if (clean.includes('—')) {\n    const [name, ...rest] = clean.split('—');\n    return { name: name.trim(), url: rest.join('—').trim() };\n  }\n\n  // Fallback: hyphen delimiter (only split on \" - \" to avoid breaking names like \"Foo-Bar\")\n  if (clean.includes(' - ')) {\n    const [name, ...rest] = clean.split(' - ');\n    return { name: name.trim(), url: rest.join(' - ').trim() };\n  }\n\n  // Fallback: try to find a domain-like token anywhere in the line\n  const m = clean.match(/\\b((?:https?:\\/\\/)?(?:www\\.)?[a-z0-9.-]+\\.[a-z]{2,}(?:\\/\\S*)?)\\b/i);\n  if (m) {\n    const rawUrl = m[1];\n    const name = clean.replace(rawUrl, '').replace(/[-—\\s]+$/, '').trim();\n    return { name: name.trim(), url: rawUrl.trim() };\n  }\n\n  // If no URL found, treat whole line as name\n  return { name: clean, url: '' };\n}\n\nconst competitorLines = competitorsText\n  .split(/\\r?\\n/)\n  .map(s => s.trim())\n  .filter(Boolean);\n\nconst competitors = competitorLines.map((line, index) => {\n  const { name, url } = splitNameAndUrl(line);\n  return {\n    id: `competitor-${index + 1}`,\n    name,\n    url: normalizeUrl(url),\n    question\n  };\n});\n\nreturn competitors.map(c => ({ json: c }));"
      },
      "id": "4f54a568-fc15-4e5b-9347-25b7c56529b0",
      "name": "Parse Competitors1",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -2688,
        960
      ]
    },
    {
      "parameters": {
        "jsCode": "// Build a numbered competitor list from all items\nconst items = $input.all();\n\nlet competitorList = '';\nlet question = '';\n\nfor (let i = 0; i < items.length; i++) {\n  const item = items[i].json;\n  if (i === 0 && item.question) question = item.question;\n\n  if (item.name && item.url) {\n    competitorList += `${i + 1}. ${item.name} (${item.url})`;\n    if (i < items.length - 1) competitorList += '\\n';\n  }\n}\n\nreturn {\n  json: {\n    competitorList,\n    question\n  }\n};"
      },
      "id": "e2769327-0426-489f-bd87-f8b326247d6f",
      "name": "Build Competitor List1",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -2464,
        1024
      ],
      "executeOnce": false
    },
    {
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "id",
          "value": "gpt-4o"
        },
        "responses": {
          "values": [
            {
              "role": "system",
              "content": "You are a competitive research planning assistant. Your job is to take a user's research question about their competitors and create specific, actionable browsing goals for an AI web agent to accomplish on each competitor's website.\n\nThe web agent will visit a URL and execute the goal you provide. It can navigate pages, click buttons, read content, and extract information.\n\nIMPORTANT:\n- \"Competitors\" means the companies listed below (the user's competitors), not the competitors of those companies.\n- Only use the provided competitor list. Do not invent new companies.\n- Goals must be specific and detailed so the agent knows exactly what to look for.\n- If the question is about pricing, direct it to the pricing page and extract plan details.\n- Ask the browsing agent to capture source URLs (including child pages it visits) where it finds evidence.\n\nYou may modify the competitor URL to point to a more specific page (e.g., /login, /pricing, /features) if that would help the agent find the information faster."
            },
            {
              "content": "=Competitors:\n{{ $json.competitorList }}\n\nUser's research question: \"{{ $json.question }}\"\n\nFor each competitor, create a specific browsing goal for the web agent. Return a JSON object with a \"goals\" array where each item has:\n- \"competitor_name\": the competitor name from the list above\n- \"competitor_url\": the URL the agent should visit (use the provided URL or a specific subpage)\n- \"goal\": detailed instructions for the browsing agent\n\nReturn ONLY valid JSON. No markdown. No code fences."
            }
          ]
        },
        "builtInTools": {},
        "options": {
          "textFormat": {
            "textOptions": {}
          },
          "temperature": 0.3
        }
      },
      "id": "de119557-b35e-4f1d-858f-cfbc19882fbc",
      "name": "Plan Research Goals1",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 2.1,
      "position": [
        -2240,
        1024
      ],
      "credentials": {
        "openAiApi": {
          "id": "23Yaf2ltaQvXsEAM",
          "name": "OpenAi account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Parse the OpenAI JSON response and extract goals (matches your OpenAI node schema)\nconst j = $input.first().json;\n\n// The JSON string is here:\n// j.output[0].content[0].text\nconst text =\n  j?.output?.[0]?.content?.find(c => c?.type === 'output_text')?.text\n  ?? j?.output?.[0]?.content?.[0]?.text\n  ?? null;\n\nif (!text || typeof text !== 'string') {\n  return [{\n    json: {\n      error: 'No output_text.text found in OpenAI response',\n      availableKeys: Object.keys(j || {}),\n      sample: j\n    }\n  }];\n}\n\nlet parsed;\ntry {\n  parsed = JSON.parse(text);\n} catch (e) {\n  // In case the model wrapped JSON in ```json fences (just in case)\n  const stripped = text.replace(/```json\\s*|```/g, '').trim();\n  parsed = JSON.parse(stripped);\n}\n\nconst goals = Array.isArray(parsed?.goals) ? parsed.goals : [];\n\nreturn goals.map(g => ({\n  json: {\n    competitor_name: g.competitor_name || '',\n    competitor_url: g.competitor_url || '',\n    goal: g.goal || ''\n  }\n}));"
      },
      "id": "aa7719c3-33fa-4ed8-8c65-a37dbb830bde",
      "name": "Parse Goals1",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1888,
        1024
      ],
      "executeOnce": true
    },
    {
      "parameters": {
        "mode": "combine",
        "advanced": true,
        "mergeByFields": {
          "values": [
            {
              "field1": "name",
              "field2": "competitor_name"
            }
          ]
        },
        "options": {}
      },
      "id": "b77f9fe5-b351-4c40-9c07-34ac9b058ac3",
      "name": "Match Competitors with Goals1",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        -1664,
        960
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Prepare Tinyfish run parameters from matched competitor and goal data\nconst item = $input.item.json;\n\nlet runUrl = item.competitor_url || item.url || '';\nif (runUrl && !runUrl.startsWith('http://') && !runUrl.startsWith('https://')) {\n  runUrl = 'https://' + runUrl;\n}\n\nlet runGoal = '';\nif (item.goal) {\n  runGoal = item.goal;\n} else {\n  const question = item.question || 'research this competitor';\n  const name = item.name || item.competitor_name || 'this competitor';\n  runGoal = `Find information relevant to: ${question} on ${name}'s website.`;\n}\n\nrunGoal += '\\n\\nWhen you find evidence, list the exact source URLs (including child pages you visited) in a \"sources\" list.';\n\nreturn {\n  json: {\n    id: item.id,\n    name: item.name || item.competitor_name,\n    url: item.url,\n    runUrl,\n    runGoal,\n    question: item.question\n  }\n};"
      },
      "id": "3dcda4f2-2aa7-4eee-95f5-8ff240737003",
      "name": "Prepare Tinyfish Goal1",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1440,
        960
      ]
    },
    {
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "id",
          "value": "gpt-4o"
        },
        "responses": {
          "values": [
            {
              "role": "system",
              "content": "=You are given research data from Tinyfish web agents that includes the research question, competitor name, url, goal for the agent, the agent research results. Please try to summarize the research results corresponding to each competitor despite the irrelavant data contained.\n\nResearch question: \"{{ $json.question }}\"\n\nData (JSON):\n{{ $json.researchPayloadText }}\n\nInstructions:\n- Use the data provided.\n  - Prefer runs whose goal mentions the competitor name OR whose sources include the competitor domain.\n- Produce:\n  1) Executive Summary (2-3 sentences)\n  2) Per-competitor answer to the research question\n  3) Comparison table (if applicable)\n  4) Key insights / opportunities\n- Do NOT fabricate sign-in methods."
            },
            {
              "content": "=Research question: \"{{ $json.question }}\"\n\nCompetitor: {{ $json.name }}\n\nRaw data from browsing their website:\n{{ $json.tinyfish_result.result }}\n\nProvide a clear, concise summary of what was found regarding the research question."
            }
          ]
        },
        "builtInTools": {},
        "options": {
          "temperature": 0.2
        }
      },
      "id": "56103846-1da0-4ddf-a883-9469719c33be",
      "name": "Summarize Competitor Result1",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 2.1,
      "position": [
        128,
        864
      ],
      "credentials": {
        "openAiApi": {
          "id": "23Yaf2ltaQvXsEAM",
          "name": "OpenAi account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Aggregate competitor context from \"Match Competitors with Goals\"\n// and Tinyfish run results from the current input (Check If Complete output).\n\n// 1) Pull ALL competitor+goal items (not just one)\nconst compItems = $(\"Match Competitors with Goals1\")?.all?.() ?? [];\nconst competitors = compItems.map((it) => {\n  const j = it.json ?? {};\n  return {\n    id: j.id ?? null,\n    name: j.name ?? j.competitor_name ?? \"\",\n    url: j.url ?? \"\",\n    runUrl: j.runUrl ?? j.competitor_url ?? j.url ?? \"\",\n    runGoal: j.runGoal ?? j.goal ?? \"\",\n    question: j.question ?? \"\",\n  };\n}).filter(c => c.name);\n\n// 2) Derive question (prefer the first non-empty)\nconst question =\n  competitors.find(c => c.question)?.question ??\n  \"\";\n\n// 3) Get ALL runs from current node input\n// Your Check If Complete input appears to be an array of run objects.\n// In n8n Code node, $input.all() returns items; each item has .json.\n// If your IF node passes the whole array as one item, it'll be in item.json.\n// Handle both shapes: [run, run] OR { data: [run, run] } OR single run.\nconst inItems = $input.all().map(i => i.json);\n\n// Flatten possible shapes into `runsRaw`\nlet runsRaw = [];\nfor (const x of inItems) {\n  if (Array.isArray(x)) {\n    runsRaw.push(...x);\n  } else if (Array.isArray(x?.data)) {\n    runsRaw.push(...x.data);\n  } else if (Array.isArray(x?.runs)) {\n    runsRaw.push(...x.runs);\n  } else if (x?.run_id || x?.status) {\n    runsRaw.push(x);\n  }\n}\n\n// 4) Normalize runs and extract useful fields\nconst runs = runsRaw.map((run) => {\n  const status = String(run.status ?? \"\").toUpperCase();\n  const resultObj = run.result ?? null;\n\n  const sources =\n    Array.isArray(resultObj?.sources) ? resultObj.sources :\n    Array.isArray(run.sources) ? run.sources :\n    null;\n\n  return {\n    run_id: run.run_id ?? null,\n    status,\n    goal: run.goal ?? \"\",\n    created_at: run.created_at ?? null,\n    started_at: run.started_at ?? null,\n    finished_at: run.finished_at ?? null,\n    error: run.error ?? null,\n    sources,\n    result: resultObj,\n    rawResultText: resultObj ? JSON.stringify(resultObj, null, 2) : \"\",\n  };\n});\n\n// 5) Optional: map results by competitor name if it exists in goal text\n// (best-effort only; you said matching is not required)\nconst byName = {};\nfor (const c of competitors) {\n  byName[c.name] = {\n    competitor: c,\n    runs: runs.filter(r => (r.goal || \"\").toLowerCase().includes(c.name.toLowerCase())),\n  };\n}\n\n// 6) Return ONE aggregated item for downstream OpenAI\nreturn [{\n  json: {\n    question,\n    competitors,\n    runs,\n    byName,\n    allDone: runs.length > 0 && runs.every(r => [\"COMPLETED\", \"FAILED\", \"CANCELLED\"].includes(r.status)),\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -320,
        784
      ],
      "id": "4468a599-8964-4beb-8d2a-ceaf0a2be104",
      "name": "Normalize Tinyfish Run1"
    },
    {
      "parameters": {
        "jsCode": "// Build Research Payload Text\nconst root = $input.first().json;\n\n// Always take arrays directly (no matching, no filtering)\nconst competitors = Array.isArray(root.competitors) ? root.competitors : [];\nconst runs = Array.isArray(root.runs) ? root.runs : [];\n\n// Build a \"no-loss\" payload for the LLM\nconst payload = {\n  question: root.question ?? competitors?.[0]?.question ?? \"\",\n  allDone: root.allDone ?? null,\n  competitorCount: competitors.length,\n  runCount: runs.length,\n  competitors,  // FULL objects\n  runs,         // FULL objects\n  byName: root.byName ?? null, // optional, keep if present\n};\n\nreturn [{\n  json: {\n    question: payload.question,\n    competitors,\n    runs,\n    allDone: payload.allDone,\n    competitorCount: payload.competitorCount,\n    runCount: payload.runCount,\n    researchPayloadText: JSON.stringify(payload, null, 2),\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -96,
        864
      ],
      "id": "51acffb4-ac55-40d2-a0b1-081968e6f72d",
      "name": "explode competitor runs1"
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        704,
        784
      ],
      "id": "87c87d8a-c4a4-460e-83a6-c15a2b4b2232",
      "name": "Merge1"
    },
    {
      "parameters": {
        "jsCode": "const root = $input.first().json;\n\n// The OpenAI node output you showed looks like:\n// [ { output: [ { content: [ { text: \"...\" } ] } ] } ]\n// But in n8n, we typically receive ONE item whose json has fields like `output`.\n\nconst output = root.output ?? root.data?.output ?? null;\n\nlet textParts = [];\n\nif (Array.isArray(output)) {\n  for (const msg of output) {\n    const content = msg?.content;\n    if (Array.isArray(content)) {\n      for (const c of content) {\n        if (typeof c?.text === 'string' && c.text.trim()) textParts.push(c.text);\n      }\n    }\n  }\n}\n\n// Fallbacks (some n8n OpenAI nodes return message/content directly)\nif (textParts.length === 0) {\n  const fallback = root.message?.content ?? root.message ?? root.text ?? root.output_text ?? '';\n  if (typeof fallback === 'string' && fallback.trim()) textParts = [fallback];\n}\n\nconst report = textParts.join('\\n\\n').trim();\n\nreturn [{\n  json: {\n    ...root,\n    report,\n    report_format: 'markdown'\n  }\n}];"
      },
      "id": "7c26ad07-87a0-47d7-baff-c64bca3398cb",
      "name": "Extract Report (OpenAI Output)1",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        480,
        864
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "q",
              "name": "question",
              "value": "={{ $json.question || '' }}",
              "type": "string"
            },
            {
              "id": "r",
              "name": "report_md",
              "value": "={{ $json.report || '' }}",
              "type": "string"
            },
            {
              "id": "c",
              "name": "competitor_names",
              "value": "={{ ($json.competitors || []).map(c => c.name).join(', ') }}",
              "type": "string"
            },
            {
              "id": "ts",
              "name": "created_at",
              "value": "={{ new Date().toISOString() }}",
              "type": "string"
            },
            {
              "id": "run",
              "name": "run_ids",
              "value": "={{ ($json.runs || []).map(r => r.run_id).join(', ') }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "6ea6fd2c-0e0c-46de-abed-36cc0f53433b",
      "name": "Prepare Report Row1",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1152,
        784
      ]
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all().map(i => i.json);\n\n// Heuristic: pick the item that has \"runs\" as research payload\nconst research = items.find(x => Array.isArray(x.runs) || Array.isArray(x.competitors)) || {};\n// pick the item that has reportMarkdown\nconst reportP = items.find(x => typeof x.report === \"string\") || {};\n\nreturn [{\n  json: {\n    ...research,\n    report: reportP.report || \"\",\n    generatedAt: new Date().toISOString(),\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        928,
        784
      ],
      "id": "ede6cfbb-1f76-4260-8a59-4b4dc6af6d0d",
      "name": "Code in JavaScript1"
    },
    {
      "parameters": {
        "operation": "runAsync",
        "url": "={{ $json.runUrl }}",
        "goal": "={{ $json.runGoal }}",
        "options": {}
      },
      "type": "n8n-nodes-tinyfish.tinyfish",
      "typeVersion": 1,
      "position": [
        -1232,
        960
      ],
      "id": "4050c56d-2c74-430c-9815-348e46fef815",
      "name": "TinyFish Web Agent1",
      "credentials": {
        "tinyfishApi": {
          "id": "fGW1Y2sIYgdmiJHf",
          "name": "TinyFish Web Agent account"
        }
      }
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "https://docs.google.com/spreadsheets/d/1nGc78CeUheZWtn_iGUD3qNusK4ZlHUb-Dib5UorBd3k/edit?gid=0#gid=0",
          "mode": "url"
        },
        "sheetName": {
          "__rl": true,
          "value": "https://docs.google.com/spreadsheets/d/1nGc78CeUheZWtn_iGUD3qNusK4ZlHUb-Dib5UorBd3k/edit?gid=0#gid=0",
          "mode": "url"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [],
          "schema": [
            {
              "id": "question",
              "displayName": "question",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "report_md",
              "displayName": "report_md",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "competitor_names",
              "displayName": "competitor_names",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "created_at",
              "displayName": "created_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "run_ids",
              "displayName": "run_ids",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "id": "baed8d37-3448-42ff-b7e1-a57da6f8af6c",
      "name": "Append Report Row (Google Sheets)",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        1376,
        944
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "nQmUsFUpNzmxpeGQ",
          "name": "Google Sheets account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const j = $json;\n\nconst question = j.question || '';\nconst competitors = j.competitor_names\n  || (Array.isArray(j.competitors) ? j.competitors.map(c => c.name).join(', ') : '')\n  || '';\n\nconst report = j.report_md || j.reportMarkdown || '';\n\nconst createdAt = j.created_at || j.createdAt || new Date().toISOString();\nconst safe = (question || 'report')\n  .replace(/[^a-z0-9-_ ]/gi, '')\n  .slice(0, 60)\n  .trim()\n  .replace(/\\s+/g, '-');\n\nconst mdFilename = `competitive-research-${safe || 'report'}-${Date.now()}.md`;\n\nconst mdContent =\n`# Competitive Research\n\n**Question:** ${question}\n**Competitors:** ${competitors}\n**Created:** ${createdAt}\n\n---\n\n${report}\n`;\n\nreturn [{\n  json: {\n    mdFilename,\n    mdContent\n  }\n}];"
      },
      "id": "9c2e355b-78e5-4ca0-87b4-bdd1d32c9859",
      "name": "Create Markdown File Content",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1376,
        1184
      ]
    },
    {
      "parameters": {
        "authentication": "oAuth2",
        "binaryData": true,
        "name": "={{ $binary.data.fileName }}",
        "options": {}
      },
      "id": "c4d55a08-5894-4679-86bc-683b863cfad5",
      "name": "Upload Markdown to Drive",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 1,
      "position": [
        1776,
        1184
      ],
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "zeH0JO9qxbwVb0pH",
          "name": "Google Drive account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const baseName = ($json.mdFilename || `competitive-research-${Date.now()}`).trim();\n\n// Force .md extension\nconst filename = baseName.toLowerCase().endsWith('.md') ? baseName : `${baseName}.md`;\n\nconst md = $json.mdContent || '';\n\nconst binaryData = await this.helpers.prepareBinaryData(\n  Buffer.from(md, 'utf8'),\n  filename,\n  'text/markdown'\n);\n\nreturn [{\n  json: { mdFilename: filename }, // optional, safe to keep\n  binary: { data: binaryData }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1584,
        1184
      ],
      "id": "b904162a-2b96-482f-8be5-ff9854f73b98",
      "name": "Convert to Binary"
    },
    {
      "parameters": {
        "jsCode": "const data = $json;\nconst content = JSON.stringify(data, null, 2);\nconst filename = `scout-results-${Date.now()}.json`;\n\nreturn [{\n  json: data,\n  binary: {\n    data: await this.helpers.prepareBinaryData(\n      Buffer.from(content, \"utf8\"),\n      filename,\n      \"application/json\"\n    )\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1376,
        784
      ],
      "id": "a36185b3-b397-4d19-9d6d-7b8113219dff",
      "name": "Save as JSON"
    }
  ],
  "pinData": {
    "Competitor Research Form1": [
      {
        "json": {
          "competitors": "Notion - www.notion.com\r\nServiceNow - www.servicenow.com",
          "question": "What sign-in methods do my competitors use?",
          "submittedAt": "2026-03-04T01:19:12.490-08:00",
          "formMode": "test"
        },
        "pairedItem": {
          "item": 0
        }
      }
    ]
  },
  "connections": {
    "Get Tinyfish Status": {
      "main": [
        [
          {
            "node": "Evaluate Runs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check If Complete": {
      "main": [
        [
          {
            "node": "Normalize Tinyfish Run1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Rehydrate RunIds for Next Poll",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 3 Seconds": {
      "main": [
        [
          {
            "node": "Get Tinyfish Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Evaluate Runs": {
      "main": [
        [
          {
            "node": "Check If Complete",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rehydrate RunIds for Next Poll": {
      "main": [
        [
          {
            "node": "Wait 3 Seconds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Competitor Research Form1": {
      "main": [
        [
          {
            "node": "Parse Competitors1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Competitors1": {
      "main": [
        [
          {
            "node": "Build Competitor List1",
            "type": "main",
            "index": 0
          },
          {
            "node": "Match Competitors with Goals1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Competitor List1": {
      "main": [
        [
          {
            "node": "Plan Research Goals1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Plan Research Goals1": {
      "main": [
        [
          {
            "node": "Parse Goals1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Goals1": {
      "main": [
        [
          {
            "node": "Match Competitors with Goals1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Match Competitors with Goals1": {
      "main": [
        [
          {
            "node": "Prepare Tinyfish Goal1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Tinyfish Goal1": {
      "main": [
        [
          {
            "node": "TinyFish Web Agent1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Summarize Competitor Result1": {
      "main": [
        [
          {
            "node": "Extract Report (OpenAI Output)1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Tinyfish Run1": {
      "main": [
        [
          {
            "node": "explode competitor runs1",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "explode competitor runs1": {
      "main": [
        [
          {
            "node": "Summarize Competitor Result1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge1": {
      "main": [
        [
          {
            "node": "Code in JavaScript1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Report (OpenAI Output)1": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Prepare Report Row1": {
      "main": [
        [
          {
            "node": "Save as JSON",
            "type": "main",
            "index": 0
          },
          {
            "node": "Append Report Row (Google Sheets)",
            "type": "main",
            "index": 0
          },
          {
            "node": "Create Markdown File Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript1": {
      "main": [
        [
          {
            "node": "Prepare Report Row1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "TinyFish Web Agent1": {
      "main": [
        [
          {
            "node": "Get Tinyfish Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Markdown File Content": {
      "main": [
        [
          {
            "node": "Convert to Binary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert to Binary": {
      "main": [
        [
          {
            "node": "Upload Markdown to Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate",
    "availableInMCP": true,
    "timeSavedMode": "fixed",
    "callerPolicy": "workflowsFromSameOwner",
    "timeSavedPerExecution": 6
  },
  "versionId": "89b8db9d-28e1-4fe6-bb09-94b14648b03c",
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "e5e32c5f2ff1f80e7ad139e29e774db1914617b769b8544eb86000082273891d"
  },
  "id": "cwvxwiya1U6tHBXg",
  "tags": []
}

================================================
FILE: N8N_WorkFlows/Competitor Scout CLI/README.md
================================================
# Competitor Scout (n8n Workflow) — Beginner Setup & Run Guide

This repository/workflow lets you research competitor feature decisions (e.g., “What sign-in methods do my competitors use?”) using:

- **n8n** for orchestration
- **OpenAI** for planning + report generation
- **Tinyfish Web Agent** for web browsing/evidence collection
- Optional exports:
  - **Google Sheets** (log results)
  - **Google Drive** (upload a Markdown report)

This guide assumes you have **zero n8n experience**.

---

## 1) What you will get

When you run the workflow, you’ll input:

- A list of competitors (one per line, `Name — URL`)
- A research question

The workflow will output:

- A **Markdown comparison report** (from OpenAI)
- Raw Tinyfish run results (evidence, sources)
- Optional exports:
  - A row appended to Google Sheets
  - A `.md` file uploaded to Google Drive

---

## 2) Prerequisites

### Accounts / keys you need
1) **OpenAI API key**
2) **Tinyfish API key**
3) **Google account** (only if exporting to Google Sheets/Drive)

### Software
- n8n (choose one)
  - **n8n Desktop / local** (easiest to start)
  - **Self-hosted** (Docker)
  - **n8n Cloud** (paid)

> If you’re not sure: start with **local n8n**. This guide uses examples for local n8n at `http://localhost:5678`.

---

## 3) Install & open n8n (beginner-friendly)

### Option A — n8n Desktop (easy)
1) Install n8n Desktop (from n8n docs)
2) Open it
3) You should see n8n running

### Option B — Docker (common)
Run:

```bash
docker run -it --rm \
  -p 5678:5678 \
  -v ~/.n8n:/home/node/.n8n \
  n8nio/n8n
````

Then open:

* `http://localhost:5678`

---

## 4) Import the workflow JSON into n8n

1. In n8n, click **Workflows**
2. Click **Import from file**
3. Select the workflow JSON provided with this project
4. Click **Save**

You should now see a workflow canvas with nodes like:

* Form Trigger
* Parse Competitors
* OpenAI planning/report nodes
* Tinyfish nodes
* Export nodes (Sheets/Drive)

---

## 5) Install the Tinyfish community node (if required)

If your workflow uses a community Tinyfish node (recommended):

1. In n8n, go to **Settings → Community Nodes**
2. Click **Install**
3. Enter the Tinyfish node package name (example):

   * `n8n-nodes-tinyfish`
4. Install
5. Restart n8n if prompted

> After installation, you should be able to add nodes named like **TinyFish Web Agent**.

---

## 6) Set up credentials (the most important part)

n8n nodes connect to external services using **Credentials**.

### 6.1 OpenAI credentials

1. Go to **Credentials**
2. Click **New**
3. Search for **OpenAI**
4. Paste your OpenAI API key
5. Save
6. Open the workflow and assign this credential to every OpenAI node

---

### 6.2 Tinyfish credentials

If using the Tinyfish node:

1. **Credentials → New**
2. Search for **TinyFish Web Agent** (or similar)
3. Paste your Tinyfish API key
4. Save
5. Assign to:

   * `TinyFish Web Agent (runAsync)`
   * `TinyFish Get Run / Status` (if present)

---

### 6.3 Google Sheets + Google Drive credentials (optional exports)

If you want exports, you must set up Google OAuth.

#### You may see an error:

**“Access blocked: … app is currently being tested … only developer-approved testers”**

Fix: Add your email as a Test User (see below).

---

## 7) Google OAuth setup (for beginners)

This is needed for:

* Google Sheets export (append rows)
* Google Drive upload (upload markdown report)

### If you use n8n Cloud

Google auth is usually one-click:

1. Open a Google Sheets node
2. Credentials → Create new → **Google Sheets OAuth2**
3. Click **Connect**
4. Log in to Google and approve

Done.

---

### If you use local/self-hosted n8n (most common)

You must create a Google OAuth app.

#### Step A — Create OAuth credentials in Google Cloud

1. Go to Google Cloud Console: `https://console.cloud.google.com/`
2. Create/select a project
3. Go to **APIs & Services → Library**
4. Enable:

   * **Google Sheets API**
   * **Google Drive API** (if uploading markdown to Drive)

#### Step B — Configure OAuth consent screen

1. **APIs & Services → OAuth consent screen**
2. Choose **External** (typical)
3. Fill required fields (App name, email)
4. Set Publishing status to **Testing** (ok)
5. Add yourself as a **Test user**:

   * Add the Google email you’ll sign in with

✅ This prevents the “Access blocked” error.

#### Step C — Create OAuth client ID

1. **APIs & Services → Credentials**
2. Click **Create Credentials → OAuth Client ID**
3. Type: **Web application**
4. Add Authorized redirect URI:

For local n8n:

* `http://localhost:5678/rest/oauth2-credential/callback`

For hosted n8n:

* `https://YOUR_DOMAIN/rest/oauth2-credential/callback`

5. Save, then copy:

* **Client ID**
* **Client Secret**

---

### Step D — Add Google credentials in n8n

1. n8n → **Credentials → New**
2. Create:

   * **Google Sheets OAuth2**
   * **Google Drive OAuth2**
3. Paste the Client ID/Secret from Google Cloud
4. Click **Connect**
5. Approve permissions in Google

✅ Done.

---

## 8) Configure exports (Sheets + Drive)

### 8.1 Google Sheets

Create a Google Sheet with two tabs:

#### Tab 1: `Reports` (headers)

* `generatedAt`
* `question`
* `competitorNames`
* `competitorCount`
* `runCount`
* `allDone`
* `reportMarkdown`

#### Tab 2: `Runs` (headers)

* `generatedAt`
* `question`
* `run_id`
* `status`
* `goal`
* `started_at`
* `finished_at`
* `sources_json`
* `result_json`

In n8n:

* Open the **Append Reports** node and select your spreadsheet + tab
* Open the **Append Runs** node and select your spreadsheet + tab
* Ensure both nodes use the correct Google Sheets credential

---

### 8.2 Google Drive upload (Markdown report)

In the workflow, the Drive upload chain should look like:

* Build Clean MD (creates `mdContent`)
* Convert MD to binary (with filename + MIME)
* Upload to Google Drive

Open **Upload MD to Drive** node:

* Select Drive credential
* Choose destination folder (optional)

---

## 9) Running the workflow

### Step 1 — Activate workflow (optional)

If you want it always available, click **Active** toggle.

### Step 2 — Run in test mode (recommended first run)

1. Open the workflow
2. Click **Execute workflow**
3. A form page opens

### Step 3 — Fill the form

Competitors (one per line):

```
Notion — https://www.notion.com
ServiceNow — https://www.servicenow.com
```

Question:

```
What sign-in methods do my competitors use?
```

Submit.

### Step 4 — Monitor progress

In n8n execution view, you’ll see:

* OpenAI planning
* Tinyfish runs
* Polling loop until completion
* OpenAI report generation
* Exports (Sheets + Drive)

---

## 10) Troubleshooting (common)

### “Access blocked: n8n has not completed verification”

Your Google OAuth consent screen is in **Testing** and your account is not in **Test users**.

Fix:

* Google Cloud Console → OAuth consent screen → **Test users** → add your email

### “runId becomes undefined during polling”

This happens if the workflow loops an aggregated item back into a node expecting per-run items.

Fix:

* Ensure your loop includes a “rehydrate runIds” step before re-polling.

### Google Sheets rows missing / misaligned

* Ensure tab names match exactly (`Reports`, `Runs`)
* Ensure headers exist
* Use Auto-map input data, or map manually

### Drive upload becomes “binary file”

Fix:

* Ensure filename ends with `.md`
* Ensure MIME is `text/markdown` or `text/plain`
* Upload using `$binary.data.fileName`

---

## 11) Safety / cost controls

* Keep competitor count small (e.g., ≤10)
* Tinyfish browsing + OpenAI calls cost money; test with 1–2 competitors first
* Avoid sending extremely large raw results to OpenAI if you hit token limits

---

## 12) Customization ideas

* Add more export targets: Slack, Notion, email
* Add a “depth” or “strictness” setting to planning prompts
* Add schema extraction (e.g., normalize sign-in methods into a boolean table)

---

## 13) Quick “Checklist” before first run

* [ ] Workflow imported
* [ ] OpenAI credential added + assigned
* [ ] Tinyfish credential added + assigned
* [ ] (Optional) Google Sheets credential connected
* [ ] (Optional) Google Drive credential connected
* [ ] Sheets tabs created (`Reports`, `Runs`)
* [ ] Workflow runs successfully with 1 competitor test

---

```
```


================================================
FILE: N8N_WorkFlows/Daily Product Hunt Tracker/Daily Product Hunt Tracker via Tinyfish.json
================================================
{
  "name": "Daily Product Hunt Tracker — TinyFish + Telegram",
  "nodes": [
    {
      "id": "schedule-trigger",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [-740, -100],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 18
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "tinyfish-extract",
      "name": "TinyFish Extract",
      "type": "n8n-nodes-tinyfish.tinyfish",
      "position": [-400, -100],
      "parameters": {
        "url": "https://www.producthunt.com",
        "goal": "Extract today's top 5 trending products from the homepage with their name, tagline, upvote count, tags, and product page URL.",
        "options": {
          "browserProfile": "stealth"
        },
        "operation": "runSse"
      },
      "credentials": {
        "tinyfishApi": {
          "id": "",
          "name": "TinyFish Web Agent API account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "if-has-data",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [-100, -100],
      "parameters": {
        "options": {
          "looseTypeValidation": true
        },
        "conditions": {
          "options": {
            "version": 2,
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "check-result",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.resultJson }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "format-agent",
      "name": "Format Report",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [180, -100],
      "parameters": {
        "text": "=Here is raw scraped data from Product Hunt. Format it into a clean Telegram message.\n\nData:\n{{ JSON.stringify($json.resultJson) }}",
        "promptType": "define",
        "batching": {}
      },
      "typeVersion": 1.7
    },
    {
      "id": "openrouter-llm",
      "name": "OpenRouter Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [180, 100],
      "parameters": {
        "options": {
          "systemMessage": "You format scraped product data into clean Telegram messages. Output ONLY the formatted message, nothing else.\n\nFormat:\n\ud83c\udfc6 Daily Product Hunt Trending Report\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\nThen for each product (ranked by upvotes, highest first):\n[medal emoji] [Name]  ([upvotes] upvotes)\n[Tagline]\n\ud83c\udff7 [comma-separated tags]\n\ud83d\udd17 [URL]\n\nUse \ud83e\udd47 \ud83e\udd48 \ud83e\udd49 for top 3, then #4 #5 etc.\n\nEnd with:\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\ud83d\udcc5 [today's date YYYY-MM-DD]\n\nDo not add any commentary. Just the formatted message."
        }
      },
      "credentials": {
        "openRouterApi": {
          "id": "",
          "name": "OpenRouter account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "telegram-send",
      "name": "Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [440, -100],
      "parameters": {
        "text": "={{ $json.text }}",
        "chatId": "YOUR_CHAT_ID",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "id": "",
          "name": ""
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "sticky-main",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [-1400, -260],
      "parameters": {
        "color": 5,
        "width": 700,
        "height": 900,
        "content": "\ud83c\udfc6 Daily Product Hunt Tracker with AI Formatting\nScrapes Product Hunt daily using TinyFish Web Agent, formats with AI, sends to Telegram.\n\n\ud83e\udded How It Works:\n\ud83d\udd54 Schedule Trigger \u2014 Runs daily at 6PM\n\ud83c\udfc6 TinyFish Web Agent \u2014 Scrapes top 5 products (stealth mode, SSE streaming)\n\ud83d\udd00 IF Node \u2014 Checks if data was returned\n\ud83e\udd16 AI Format \u2014 OpenRouter LLM formats the raw data into a clean report\n\ud83d\udcf2 Telegram \u2014 Sends the formatted report to your chat\n\n\u2705 Why this approach:\n- TinyFish goal is simple plain English \u2014 no rigid schema\n- AI handles any JSON structure \u2014 no brittle key matching\n- Clean, consistent Telegram output every time\n\n\ud83d\udd27 Requirements:\n\u2705 TinyFish API Key (https://agent.tinyfish.ai/api-keys)\n\u2705 OpenRouter API Key (https://openrouter.ai/keys)\n\u2705 Telegram Bot Token & Chat ID\n\n\ud83d\udee0 Customization:\n- Change URL and goal to scrape any site\n- Swap Telegram for Discord, Slack, email, or Notion\n- Change the AI system prompt to format differently"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-trigger",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [-840, -260],
      "parameters": {
        "width": 260,
        "height": 420,
        "content": "Scheduled Trigger"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-tinyfish",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [-500, -260],
      "parameters": {
        "color": 3,
        "width": 320,
        "height": 420,
        "content": "TinyFish Web Agent\nSimple English goal. No schema needed."
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-result",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [-160, -260],
      "parameters": {
        "color": 5,
        "width": 560,
        "height": 420,
        "content": "AI Format & Send to Telegram"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Schedule Trigger": {
      "main": [
        [
          { "node": "TinyFish Extract", "type": "main", "index": 0 }
        ]
      ]
    },
    "TinyFish Extract": {
      "main": [
        [
          { "node": "If", "type": "main", "index": 0 }
        ]
      ]
    },
    "If": {
      "main": [
        [
          { "node": "Format Report", "type": "main", "index": 0 }
        ],
        []
      ]
    },
    "Format Report": {
      "main": [
        [
          { "node": "Telegram", "type": "main", "index": 0 }
        ]
      ]
    },
    "OpenRouter Chat Model": {
      "ai_languageModel": [
        [
          { "node": "Format Report", "type": "ai_languageModel", "index": 0 }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "active": false,
  "pinData": {}
}

================================================
FILE: N8N_WorkFlows/Daily Product Hunt Tracker/README.md
================================================
# Daily Product Hunt Tracker (n8n Workflow) — Setup & Run Guide

Get a daily Telegram message with the top 5 trending products on Product Hunt, automatically scraped and formatted by AI.

No APIs to parse, no brittle selectors — just tell TinyFish what you want in plain English, and an LLM formats it into a clean report.

---

## What you get

Every day at 6 PM, a message like this lands in your Telegram:

```
🏆 Daily Product Hunt Trending Report
━━━━━━━━━━━━━━━━━━━━━━━

🥇 CoolApp  (842 upvotes)
AI-powered workflow builder for teams
🏷 Productivity, AI, No-Code
🔗 https://www.producthunt.com/posts/coolapp

🥈 FastDB  (631 upvotes)
The database that scales itself
🏷 Developer Tools, Database, Infrastructure
🔗 https://www.producthunt.com/posts/fastdb

...

━━━━━━━━━━━━━━━━━━━━━━━
📅 2026-03-11
```

---

## How it works

```
Schedule Trigger (daily at 6 PM)
  |
  v
TinyFish Extract (scrapes Product Hunt homepage, stealth + SSE)
  |
  v
IF Node (checks if data was returned)
  |
  v
Format Report (OpenRouter LLM formats raw data into Telegram message)
  |
  v
Telegram (sends the formatted report to your chat)
```

1. **Schedule Trigger** fires daily at 6 PM (configurable)
2. **TinyFish** visits `producthunt.com` in stealth mode and extracts the top 5 products — name, tagline, upvotes, tags, and URL
3. **IF node** checks the scrape returned data (skips if empty)
4. **OpenRouter LLM** takes the raw JSON and formats it into a clean, emoji-rich Telegram message
5. **Telegram node** sends the message to your chat or group

---

## Prerequisites

### API keys you need

| Service | What for | Get it at |
|---------|----------|-----------|
| **TinyFish** | Web scraping | [agent.tinyfish.ai/api-keys](https://agent.tinyfish.ai/api-keys) |
| **OpenRouter** | LLM formatting | [openrouter.ai/keys](https://openrouter.ai/keys) |
| **Telegram** | Message delivery | Talk to [@BotFather](https://t.me/BotFather) on Telegram |

### Software

- **n8n** (Desktop, Docker, or Cloud)
- **n8n-nodes-tinyfish** community node

---

## 1) Install & open n8n

### Option A — n8n Desktop

1. Download and install n8n Desktop
2. Open it — runs at `http://localhost:5678`

### Option B — Docker

```bash
docker run -it --rm \
  -p 5678:5678 \
  -v ~/.n8n:/home/node/.n8n \
  n8nio/n8n
```

---

## 2) Install the TinyFish community node

1. In n8n, go to **Settings > Community Nodes**
2. Click **Install**
3. Enter: `n8n-nodes-tinyfish`
4. Install and restart n8n if prompted

---

## 3) Import the workflow

1. In n8n, click **Workflows > Import from file**
2. Select `Daily Product Hunt Tracker via Tinyfish.json`
3. Click **Save**

You should see these nodes on the canvas:
- **Schedule Trigger**
- **TinyFish Extract**
- **If** (data check)
- **Format Report** (LLM chain) + **OpenRouter Chat Model**
- **Telegram**

---

## 4) Set up credentials

### 4.1 TinyFish

1. **Credentials > New > TinyFish Web Agent**
2. Paste your TinyFish API key
3. Save and assign to the **TinyFish Extract** node

### 4.2 OpenRouter

1. **Credentials > New > OpenRouter**
2. Paste your OpenRouter API key
3. Save and assign to the **OpenRouter Chat Model** node

### 4.3 Telegram Bot

#### Create a bot

1. Open Telegram and search for **@BotFather**
2. Send `/newbot` and follow the prompts
3. Copy the **bot token** BotFather gives you

#### Get your Chat ID

1. Start a chat with your new bot (send it any message)
2. Visit `https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates` in your browser
3. Find `"chat":{"id":123456789}` — that number is your Chat ID

> For group chats: add the bot to the group, send a message, then check `getUpdates`. Group chat IDs are negative numbers.

#### Configure in n8n

1. **Credentials > New > Telegram API**
2. Paste your bot token
3. Save and assign to the **Telegram** node
4. Open the **Telegram** node and replace `YOUR_CHAT_ID` with your actual Chat ID

---

## 5) Running the workflow

### Test run (immediate)

1. Open the workflow
2. Click **Execute workflow**
3. Watch TinyFish scrape Product Hunt, the LLM format the data, and Telegram send the message
4. Check your Telegram — you should have a report

### Activate for daily runs

1. Toggle the **Active** switch in the top right
2. The workflow will now run automatically every day at 6 PM

> To change the time: open the **Schedule Trigger** node and adjust `triggerAtHour`.

---

## Customization ideas

- **Change the schedule**: Run every 12 hours, every Monday, or on a cron expression
- **Scrape a different site**: Change the URL and goal in TinyFish — works with any website
- **Swap the output**: Replace Telegram with Discord (webhook), Slack, email, Notion, or Google Sheets
- **Track more products**: Change the goal to "top 10" instead of "top 5"
- **Add filtering**: Add a Code node after TinyFish to filter by category, minimum upvotes, etc.
- **Change the LLM**: Swap the OpenRouter model — any model works for formatting

---

## Troubleshooting

### No Telegram message received

- Make sure you started a conversation with the bot first (send it any message)
- Verify the Chat ID is correct — revisit the `getUpdates` URL
- For group chats, make sure the bot has permission to send messages

### TinyFish returns empty data

- Product Hunt may have changed their layout — try adjusting the goal text
- Check your TinyFish API key and remaining credits
- Try running manually to see the raw output

### LLM formatting looks wrong

- The AI system prompt defines the exact format — edit it in the **OpenRouter Chat Model** node
- If products are out of order, add "ranked by upvotes, highest first" to the prompt

### Schedule not firing

- Make sure the workflow is toggled **Active**
- Check that your n8n instance is running at the scheduled time (Docker containers that stop won't trigger)

---

## Quick checklist before first run

- [ ] Workflow imported
- [ ] TinyFish credential added and assigned
- [ ] OpenRouter credential added and assigned
- [ ] Telegram bot created via @BotFather
- [ ] Chat ID obtained and set in the Telegram node
- [ ] Test execution succeeds
- [ ] Workflow toggled Active for daily runs


================================================
FILE: N8N_WorkFlows/Web Research Agent/README.md
================================================
# Web Research Agent (n8n Workflow) — Setup & Run Guide

Chat with an AI agent that scrapes any website and gives you a summarized report, saved automatically to Notion.

Ask it things like:
- "What do people on Reddit think about Arc browser?"
- "Find pricing for Linear vs Jira vs Asana"
- "What are the top complaints about Notion on Reddit?"

The agent decides where to look, scrapes the page with TinyFish, analyzes the results, and saves a clean report to your Notion workspace.

---

## How it works

```
You (chat message)
  |
  v
Web Agent (LangChain AI Agent)
  |-- uses OpenRouter (Claude Haiku 4.5) for reasoning
  |-- uses TinyFish Web Agent tool for live web scraping
  |
  v
Create Notion Report (saves output as a new Notion page)
```

1. **You ask a question** in the n8n chat UI
2. **The AI agent decides** the best URL and extraction goal based on your question
   - For Reddit questions, it searches `old.reddit.com`
   - For anything else, it constructs the right URL (pricing pages, review sites, docs, etc.)
3. **TinyFish scrapes the page** in stealth mode and returns the raw content
4. **The agent analyzes** the scraped data and produces a concise summary
5. **A Notion page is created** with the full report, titled with your query and the date

---

## Prerequisites

### Accounts / API keys you need

| Service | What for | Get it at |
|---------|----------|-----------|
| **TinyFish** | Web scraping agent | [agent.tinyfish.ai/api-keys](https://agent.tinyfish.ai/api-keys) |
| **OpenRouter** | LLM access (Claude Haiku 4.5) | [openrouter.ai/keys](https://openrouter.ai/keys) |
| **Notion** | Report storage | [notion.so/my-integrations](https://www.notion.so/my-integrations) |

### Software

- **n8n** (Desktop, Docker, or Cloud) — see [n8n docs](https://docs.n8n.io/) if you need help installing
- **n8n-nodes-tinyfish** community node (installed inside n8n)

---

## 1) Install & open n8n

### Option A — n8n Desktop (easiest)

1. Download and install n8n Desktop
2. Open it — n8n runs at `http://localhost:5678`

### Option B — Docker

```bash
docker run -it --rm \
  -p 5678:5678 \
  -v ~/.n8n:/home/node/.n8n \
  n8nio/n8n
```

Then open `http://localhost:5678`.

---

## 2) Install the TinyFish community node

1. In n8n, go to **Settings > Community Nodes**
2. Click **Install**
3. Enter: `n8n-nodes-tinyfish`
4. Install and restart n8n if prompted

---

## 3) Import the workflow

1. In n8n, click **Workflows > Import from file**
2. Select `Web Research Agent via Tinyfish.json`
3. Click **Save**

You should see 4 nodes on the canvas:
- **When chat message received** (trigger)
- **Web Agent** (LangChain AI agent)
- **OpenRouter Chat Model** + **TinyFish Web Agent** (connected as sub-nodes)
- **Create Notion Report** (output)

---

## 4) Set up credentials

### 4.1 TinyFish

1. Go to **Credentials > New**
2. Search for **TinyFish Web Agent**
3. Paste your TinyFish API key
4. Save
5. Open the workflow and assign this credential to the **TinyFish Web Agent** node

### 4.2 OpenRouter

1. **Credentials > New**
2. Search for **OpenRouter**
3. Paste your OpenRouter API key
4. Save
5. Assign to the **OpenRouter Chat Model** node

> The workflow defaults to `anthropic/claude-haiku-4.5`. You can change the model in the node settings — OpenRouter supports hundreds of models.

### 4.3 Notion

1. Go to [notion.so/my-integrations](https://www.notion.so/my-integrations)
2. Click **New integration**
3. Give it a name (e.g., "n8n Web Research")
4. Copy the **Internal Integration Secret**
5. In Notion, open the parent page where you want reports saved
6. Click **...** > **Connections** > add your integration
7. In n8n: **Credentials > New > Notion API**
8. Paste the integration secret
9. Save and assign to the **Create Notion Report** node
10. Update the **Page ID** in the node to point to your own Notion page

---

## 5) Running the workflow

1. Open the workflow in n8n
2. Click **Execute workflow** (or toggle **Active** to keep it running)
3. The n8n chat panel opens — type your question:

```
What do people on Reddit think about Cursor IDE?
```

4. The agent will:
   - Decide to search Reddit for "Cursor IDE"
   - Call TinyFish to scrape the search results
   - Summarize the findings
   - Save a report to Notion

5. You'll see the response in the chat and a new page in your Notion workspace

---

## Example queries

| Query | What happens |
|-------|-------------|
| "What does Reddit think about Supabase vs Firebase?" | Scrapes Reddit search, summarizes community sentiment |
| "Find pricing for Vercel Pro vs Netlify Pro" | Visits pricing pages and compares plans |
| "What are common complaints about Slack on Reddit?" | Searches Reddit for Slack complaints, categorizes themes |
| "Summarize the top posts on Hacker News right now" | Scrapes Hacker News front page |

---

## Customization ideas

- **Change the LLM**: Swap `anthropic/claude-haiku-4.5` for any model on OpenRouter (GPT-4o, Llama, Mistral, etc.)
- **Change the output**: Replace the Notion node with Slack, Google Sheets, email, or any other n8n node
- **Add memory**: Attach an n8n memory node to the agent for multi-turn conversations
- **Adjust the system prompt**: Edit the Web Agent's system message to change behavior, output format, or target sites

---

## Troubleshooting

### "TinyFish returned no data"

- The site may be blocking scraping — try a different URL or query
- Check your TinyFish API key is valid and has remaining credits

### Notion page not created

- Make sure your integration is connected to the parent page in Notion
- Verify the Page ID in the **Create Notion Report** node matches your workspace

### OpenRouter errors

- Check your API key and account balance at [openrouter.ai](https://openrouter.ai)
- Some models may have rate limits — try a different model if one fails

---

## Quick checklist before first run

- [ ] Workflow imported
- [ ] TinyFish credential added and assigned
- [ ] OpenRouter credential added and assigned
- [ ] Notion integration created and connected to parent page
- [ ] Notion Page ID updated in the Create Notion Report node
- [ ] Test with a simple query like "What is TinyFish?"


================================================
FILE: N8N_WorkFlows/Web Research Agent/Web Research Agent via Tinyfish.json
================================================
{
  "nodes": [
    {
      "parameters": {
        "options": {}
      },
      "id": "6b956bb9-d6c3-46dc-abf0-3b253aef99b5",
      "name": "When chat message received",
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "typeVersion": 1.1,
      "position": [
        2944,
        256
      ],
      "webhookId": "reddit-consensus-chat"
    },
    {
      "parameters": {
        "model": "anthropic/claude-haiku-4.5",
        "options": {}
      },
      "id": "50c633c7-8347-4e38-b20e-708393ab8cb6",
      "name": "OpenRouter Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        3088,
        480
      ],
      "credentials": {
        "openRouterApi": {
          "id": "93xqbFvgDLszbEx2",
          "name": "OpenRouter account"
        }
      }
    },
    {
      "parameters": {
        "pageId": {
          "__rl": true,
          "value": "https://www.notion.so/Reports-32066186caec802abbaef57607a62f9b?source=copy_link",
          "mode": "url"
        },
        "title": "=Report — {{ $('When chat message received').item.json.chatInput }} — {{ new Date().toISOString().split('T')[0] }}",
        "blockUi": {
          "blockValues": [
            {
              "richText": true,
              "text": {
                "text": [
                  {
                    "text": "={{ $json.output }}",
                    "annotationUi": {}
                  }
                ]
              }
            }
          ]
        },
        "options": {}
      },
      "id": "d71b9fd1-0cc1-4308-b6fb-377dd7daf769",
      "name": "Create Notion Report",
      "type": "n8n-nodes-base.notion",
      "typeVersion": 2.2,
      "position": [
        3440,
        256
      ],
      "credentials": {
        "notionApi": {
          "id": "zy8vYCBko9zNEyFW",
          "name": "Notion account"
        }
      }
    },
    {
      "parameters": {
        "content": "# Web Research Agent (TinyFish Web Agent)\n\nChat with an AI agent that scrapes any website and gives you a summarized report.\n\n## How It Works\n1. **You ask** — \"What do people think about [topic]?\" or \"Find pricing for [product]\"\n2. **Agent scrapes** — TinyFish Web Agent browses the relevant site with stealth mode\n3. **Agent analyzes** — OpenRouter LLM synthesizes the findings\n4. **Saved** — Creates a Notion page with the full report\n\n## Setup\n1. **TinyFish API Key** — Get at https://agent.tinyfish.ai/api-keys\n2. **OpenRouter API Key** — Get at https://openrouter.ai/keys\n3. **Notion** — Create an internal integration at notion.so/my-integrations, connect a parent page\n4. Connect all credentials and test with the chat!",
        "height": 680,
        "width": 760
      },
      "id": "30881dd3-5d01-4f5e-8576-ffde9aee9d94",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        2096,
        16
      ]
    },
    {
      "parameters": {
        "url": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('URL', ``, 'string') }}",
        "goal": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Goal', ``, 'string') }}",
        "options": {
          "browserProfile": "stealth"
        }
      },
      "type": "n8n-nodes-tinyfish.tinyfishTool",
      "typeVersion": 1,
      "position": [
        3392,
        512
      ],
      "id": "3f136c27-1d85-457c-8ae5-d40f909df960",
      "name": "TinyFish Web Agent1",
      "credentials": {
        "tinyfishApi": {
          "id": "mbrt8uD2ZV0tqtAh",
          "name": "TinyFish Web Agent account"
        }
      }
    },
    {
      "parameters": {
        "options": {
          "systemMessage": "You are an intelligent web research analyst. When the user asks you to find\n  opinions, consensus, reviews, or information on any topic:\n\n  1. Analyze the user's message and decide the best URL and goal to pass to\n  the TinyFish Web Agent tool.\n     - For Reddit: use\n  https://old.reddit.com/search/?q=YOUR_SEARCH_TERMS&sort=relevance&t=month\n     - For any other website: construct the appropriate URL based on what the\n  user asks.\n     - You decide the search terms, URL, and extraction goal based on the\n  user's intent.\n  2. In the goal, tell TinyFish exactly what to extract (titles, content,\n  links, prices, reviews — whatever is relevant).\n  3. Once you receive the scraped data, analyze it and give the user a clear,\n  useful summary.\n  4. Format your response in plain text only. No markdown, no bold, no\n  headers, no bullet symbols. Use line breaks and spacing to organize\n  sections. Label sections like TOPIC:, SUMMARY:, KEY THEMES:, NOTABLE\n  SOURCES:, DISSENTING VIEWS:.\n\n  Always use stealth browser profile when calling TinyFish.\n  If the scrape returns no data, tell the user and suggest refining their\n  query.\n\n  IMPORTANT: Keep your entire response under 2000 characters. Be concise. Do\n  NOT use any markdown formatting — no asterisks, no hashtags, no dashes for\n  bullets."
        }
      },
      "id": "98efb616-3fe2-47b6-a986-f8d0bf907eb9",
      "name": "Web Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 1.7,
      "position": [
        3184,
        256
      ]
    }
  ],
  "connections": {
    "When chat message received": {
      "main": [
        [
          {
            "node": "Web Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Web Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "TinyFish Web Agent1": {
      "ai_tool": [
        [
          {
            "node": "Web Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Web Agent": {
      "main": [
        [
          {
            "node": "Create Notion Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "93ae535be561fa96a49e2c0436c691a052a1b8a9118da8ac44dd7791f8b097b9"
  }
}

================================================
FILE: N8N_WorkFlows/ai-competitor-analysis/README.md
================================================
# AI Product Idea Generator

Reddit pain points → Product Hunt gap analysis → AI-powered product ideas.

This n8n workflow scrapes Reddit r/SaaS for real user frustrations, searches Product Hunt for existing solutions, then uses Google Gemini to identify unsolved problems and suggest specific products to build.

## How It Works

```
[Click to Run] → [TinyFish: Reddit Pain Points] → [TinyFish: Search Product Hunt] → [Gemini: Product Ideas]
```

1. **TinyFish scrapes Reddit r/SaaS** — extracts top 15 pain points with search keywords
2. **TinyFish searches Product Hunt** — uses those keywords to find which problems already have solutions and which are unsolved gaps
3. **Gemini analyzes everything** — outputs "Already Solved (skip)", "Market Gaps (build these)" with product name, pricing model, difficulty, and a "Top Pick" recommendation

## Prerequisites

- [Docker](https://docs.docker.com/get-docker/) and a lightweight runtime like [Colima](https://github.com/abetterinternet/colima) (macOS)
- A [TinyFish API key](https://agent.tinyfish.ai/signup) (free, 500 steps included)
- A [Google Gemini API key](https://aistudio.google.com/apikey) (free tier available)

## Setup

### 1. Start n8n with Docker

```bash
# macOS without Docker Desktop — install Colima first
brew install colima
colima start --cpu 2 --memory 4

# Start n8n
mkdir -p ~/.n8n
docker run -d \
  --name n8n \
  --restart unless-stopped \
  -p 5678:5678 \
  -v ~/.n8n:/home/node/.n8n \
  n8nio/n8n
```

Open http://localhost:5678 and create your owner account.

### 2. Install the TinyFish Community Node

The n8n Docker image doesn't include build tools, so we build the node in a separate container using the exact same Node.js version.

```bash
# Check the Node version inside the n8n image
docker run --rm --entrypoint /bin/sh n8nio/n8n -c "node --version"
# Example output: v24.13.1

# Build the community node using that exact version
docker run --rm -v ~/.n8n/nodes:/out node:24.13.1-alpine sh -c "
  apk add python3 make g++ &&
  cd /out &&
  npm init -y 2>/dev/null &&
  npm install n8n-nodes-tinyfish --ignore-scripts &&
  rm -rf node_modules/isolated-vm &&
  echo 'Done'
"

# Restart n8n to pick up the new node
docker restart n8n
```

> **Why `--ignore-scripts` and removing `isolated-vm`?**
> The `isolated-vm` native module is used by n8n's Code node sandbox, not by TinyFish itself. It causes segfaults in the hardened Alpine Docker image. Removing it has zero impact on the TinyFish node — all other nodes continue to work normally.

### 3. Import the Workflow

1. Open http://localhost:5678
2. Go to **Workflows → Import from File**
3. Select `ai-competitor-radar.json`

### 4. Add API Credentials

**TinyFish:**
1. Click on either TinyFish node → **Credential** → **Create New**
2. Name: `TinyFish Web Agent API`
3. Paste your API key from [agent.tinyfish.ai/api-keys](https://agent.tinyfish.ai/api-keys)

**Google Gemini:**
1. Click on the Gemini node → **Credential** → **Create New**
2. Name: `Google Gemini (PaLM) API`
3. Paste your API key from [aistudio.google.com/apikey](https://aistudio.google.com/apikey)

### 5. Run It

Click **Test Workflow**. Click the Gemini node to see the output.

## Customization

- **Change the subreddit**: Edit the URL in the first TinyFish node (e.g., `r/startups`, `r/entrepreneur`, `r/webdev`)
- **Change the source**: Swap Reddit for Hacker News, Indie Hackers, or any other site
- **Add more sources**: Duplicate a TinyFish node, change the URL + goal, and wire it in
- **Switch AI model**: Change the model in the Gemini node dropdown (e.g., `gemini-2.5-pro` for deeper analysis)

## Stopping and Restarting

```bash
# Stop
docker stop n8n

# Start again
docker start n8n

# View logs
docker logs -f n8n

# Remove completely
docker stop n8n && docker rm n8n
```


================================================
FILE: N8N_WorkFlows/ai-competitor-analysis/ai-competitor-radar.json
================================================
{
  "name": "AI Product Idea Generator — Reddit → Product Hunt → Gemini",
  "nodes": [
    {
      "parameters": {},
      "id": "a0000001-0000-0000-0000-000000000001",
      "name": "Click to Run",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [0, 300]
    },
    {
      "parameters": {
        "operation": "runSse",
        "url": "https://www.reddit.com/r/SaaS/top/?t=week",
        "goal": "Extract the top 15 posts from this subreddit. For each post, identify the core problem or frustration the user is facing. Return as a JSON object: { painPoints: [{ title: string, upvotes: number, comments: number, problem: string, keywords: string }] } where 'problem' is a clear one-sentence description of the pain point, and 'keywords' is 2-3 search terms someone would use to find a solution to this problem.",
        "options": {
          "browserProfile": "stealth"
        }
      },
      "id": "a0000001-0000-0000-0000-000000000002",
      "name": "TinyFish: Reddit Pain Points",
      "type": "n8n-nodes-tinyfish.tinyfish",
      "typeVersion": 1,
      "position": [300, 300],
      "credentials": {
        "tinyfishApi": {
          "id": "",
          "name": "TinyFish Web Agent API"
        }
      }
    },
    {
      "parameters": {
        "operation": "runSse",
        "url": "https://www.producthunt.com",
        "goal": "=I need you to search Product Hunt for existing products that solve these specific problems from Reddit:\n\n{{ JSON.stringify($json) }}\n\nFor EACH pain point above, use the search bar on Product Hunt to find if a product already exists that solves it. Search using the keywords provided.\n\nReturn a JSON object: { existingSolutions: [{ redditProblem: string, productName: string, productTagline: string, productUrl: string, howItSolves: string }], unsolvedProblems: [{ problem: string, keywords: string, whyUnsolved: string }] }\n\nIf no product exists for a pain point, add it to unsolvedProblems. Be thorough — search at least 5 of the top pain points.",
        "options": {
          "browserProfile": "stealth"
        }
      },
      "id": "a0000001-0000-0000-0000-000000000003",
      "name": "TinyFish: Search Product Hunt",
      "type": "n8n-nodes-tinyfish.tinyfish",
      "typeVersion": 1,
      "position": [600, 300],
      "credentials": {
        "tinyfishApi": {
          "id": "",
          "name": "TinyFish Web Agent API"
        }
      }
    },
    {
      "parameters": {
        "resource": "text",
        "operation": "message",
        "modelId": {
          "__rl": true,
          "value": "models/gemini-2.5-flash",
          "mode": "list",
          "cachedResultName": "Gemini 2.5 Flash"
        },
        "messages": {
          "values": [
            {
              "content": "=You are a startup product strategist. I ran two research steps:\n\nSTEP 1 — Scraped Reddit r/SaaS for real pain points people are complaining about this week:\n{{ JSON.stringify($('TinyFish: Reddit Pain Points').first().json, null, 2) }}\n\nSTEP 2 — Searched Product Hunt to see which of those pain points already have solutions, and which are UNSOLVED:\n{{ JSON.stringify($('TinyFish: Search Product Hunt').first().json, null, 2) }}\n\nNow give me:\n\n## 1. Already Solved (Skip These)\nList pain points that already have a good Product Hunt solution. One line each.\n\n## 2. Market Gaps (Build These)\nFor each UNSOLVED pain point, propose a specific product:\n- **Product Name**: A catchy name\n- **One-Liner**: What it does in one sentence\n- **Target User**: Who exactly would pay for this\n- **Revenue Model**: How it makes money (pricing, tiers)\n- **Why Now**: Why this is the right moment to build it\n- **Estimated Difficulty**: Easy/Medium/Hard to build as MVP\n\n## 3. Top Pick\nWhich single product idea has the best ratio of high demand + low competition + easy to build? Explain in 2-3 sentences why you'd bet on this one.\n\nBe specific. Use real data from the research above. No hand-waving.",
              "role": "user"
            }
          ]
        },
        "simplify": true,
        "jsonOutput": false,
        "options": {
          "systemMessage": "You are a veteran startup advisor with 20 years of experience building and investing in SaaS products. You only recommend ideas backed by evidence. You are blunt, specific, and never give generic advice. Every recommendation must reference actual data from the research provided.",
          "maxOutputTokens": 3000,
          "temperature": 0.5
        }
      },
      "id": "a0000001-0000-0000-0000-000000000005",
      "name": "Gemini: Product Ideas",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "typeVersion": 1.1,
      "position": [900, 300],
      "credentials": {
        "googlePalmApi": {
          "id": "",
          "name": "Google Gemini (PaLM) API"
        }
      }
    }
  ],
  "connections": {
    "Click to Run": {
      "main": [
        [
          { "node": "TinyFish: Reddit Pain Points", "type": "main", "index": 0 }
        ]
      ]
    },
    "TinyFish: Reddit Pain Points": {
      "main": [
        [
          { "node": "TinyFish: Search Product Hunt", "type": "main", "index": 0 }
        ]
      ]
    },
    "TinyFish: Search Product Hunt": {
      "main": [
        [
          { "node": "Gemini: Product Ideas", "type": "main", "index": 0 }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "tags": [],
  "triggerCount": 0
}


================================================
FILE: README.md
================================================
# The TinyFish Cookbook

<a href="https://www.tinyfish.ai/accelerator">
  <img width="1920" height="1080" alt="Tinyfish Accelerator banner" src="https://github.com/user-attachments/assets/bc32bf8b-1a9e-41ea-b690-4bacf41ee132" />
</a>
---

<div align="center">

<table>
<tr>
<td align="center">

### ⛊ &nbsp;&nbsp; **The TinyFish Accelerator is now accepting applications**  &nbsp;&nbsp;  ⛊

*$2M investment seed pool💰* • *9-week program* • *Free credits* • *Engineering support* • *Business mentorship* 

### **[👉 Apply Now 👈](https://www.tinyfish.ai/accelerator)**

</td>
</tr>
</table>

</div>


<div align="center">

[![Website](https://img.shields.io/badge/Website-141414?style=for-the-badge&logo=googlechrome&logoColor=white)](https://tinyfish.ai/)
[![Docs](https://img.shields.io/badge/Docs-526CE5?style=for-the-badge&logo=readthedocs&logoColor=white)](https://docs.tinyfish.ai/)
[![Discord](https://img.shields.io/badge/Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/tinyfish)
[![License](https://img.shields.io/badge/License-View-green?style=for-the-badge)](LICENSE)
[![X](https://img.shields.io/badge/X-000000?style=for-the-badge&logo=x&logoColor=white)](https://x.com/Tiny_Fish)
[![LinkedIn](https://img.shields.io/badge/LinkedIn-0A66C2?style=for-the-badge&logo=linkedin&logoColor=white)](https://www.linkedin.com/company/tinyfish-ai/)
[![Threads](https://img.shields.io/badge/Threads-000000?style=for-the-badge&logo=threads&logoColor=white)](https://www.threads.com/@tinyfish_ai)
[![Instagram](https://img.shields.io/badge/Instagram-E4405F?style=for-the-badge&logo=instagram&logoColor=white)](https://www.instagram.com/tinyfish_ai/)

</div>




## About This Repository

Welcome to the **TinyFish Cookbook!** This is a growing collection of recipes, demos, and automations built with TinyFish.

**🏆 We're SOTA!** — we just scored 90% on Mind2Web benchmark, outperforming Gemini by 21 points, OpenAI by 29, and Anthropic by 34. We ran all 300 tasks in parallel and published every single run publicly. [Read our benchmark results →](https://tinyfish.ai/blog/mind2web) | [View all runs →](https://docs.google.com/spreadsheets/d/1jgRESVlSYygPO4dKKqzPohGUX5b78Ay59422mM29CsU/edit?gid=436688783#gid=436688783)

## What is TinyFish?

**SOTA web agents in an API** that lets you treat real websites like programmable surfaces. Instead of juggling headless browsers, selectors, proxies, and weird edge cases, you call a single API with a goal and some URLs and get back clean JSON. It handles navigation, forms, filters, dynamic content, proxies, and multi-step flows across many sites at once.

The same infrastructure and agents used by big enterprises (like Google, Doordash and Classpass), now for everyone!


## Why TinyFish?
- 🕸️ **Fully managed browser and agent infra in one API**
- 🌐 **Any website → API** — Turn sites without APIs into programmable data sources
- 💬 **Natural language goals** — Send a URL + plain English, get structured JSON back
- 🤖 **Real browser automation** — Multi-step flows, forms, filters, calendars, dynamic content
- 🥷 **Built-in stealth mode** — Rotating proxies + stealth profiles included (no extra cost)
- 📊 **Production-grade logs** — Full observability and debugging for every run
- 🔌 **Flexible integration** — HTTP API, visual Playground, or MCP server for Claude/Cursor

## The Recipes

Each folder in this repo is a standalone project. Dive in to see how to solve real-world problems.

| Recipe | Description |
|--------|-------------|
| [anime-watch-hub](./anime-watch-hub) | Helps you find sites to read/watch your favorite manga/anime for free |
| [bestbet](./bestbet) | Sports betting odds comparison tool |
| [code-reference-finder](./code-reference-finder) | AI-powered code snippet analyzer that finds real-world usage examples from GitHub and Stack Overflow |
| [competitor-analysis](./competitor-analysis) | Live competitive pricing intelligence dashboard |
| [competitor-scout-cli](./competitor-scout-cli) | Natural language CLI tool for researching competitor feature decisions across multiple websites |
| [concept-discovery-system](./concept-discovery-system) | Project idea validator that discovers similar existing projects across GitHub, Dev.to, and Stack Overflow |
| [fast-qa](./fast-qa) | No-code QA testing platform with parallel test execution and live browser previews |
| [game-buying-guide](./game-buying-guide) | Video game buying decision tool comparing pricing and deals across 10 gaming platforms in parallel |
| [lego-hunter](./lego-hunter) | Global inventory search tool finding rare Lego sets across 15+ retailers with price and availability analysis |
| [loan-decision-copilot](./loan-decision-copilot) | AI-powered loan comparison tool across banks and regions |
| [logistics-sentry](./logistics-sentry) | Logistics intelligence platform for port congestion and carrier risk tracking |
| [Manga-Availability-Finder](./Manga-Availability-Finder) | Searches multiple reading platforms for manga/webtoon availability |
| [openbox-deals](./openbox-deals) | Real-time open-box and refurbished deal aggregator across 8 retailers |
| [research-sentry](./research-sentry) | Voice-first academic research co-pilot scanning ArXiv, PubMed, and more |
| [restaurant-comparison-tool](./restaurant-comparison-tool) | Pre-visit restaurant safety intelligence tool analyzing Google Maps reviews, menus, and allergen signals |
| [scholarship-finder](./scholarship-finder) | AI-powered scholarship discovery system pulling live data from official websites |
| [silicon-signal](./silicon-signal) | Semiconductor supply chain tracker for lifecycle, availability, and lead-time signals |
| [stay-scout-hub](./stay-scout-hub) | Searches across all sites for places to stay when traveling for conventions or events |
| [summer-school-finder](./summer-school-finder) | Discover and compare summer school programs from universities around the world |
| [tenders-finder](./tenders-finder) | AI-powered Singapore government tender discovery tool scraping multiple tender portals in parallel |
| [tinyskills](./tinyskills) | Multi-source AI skill guide generator |
| [tutor-finder](./tutor-finder) | AI-powered tutor discovery platform for competitive exams across multiple platforms |
| [viet-bike-scout](./viet-bike-scout) | Motorbike rental price comparison tool across Vietnamese cities using parallel browser agents |
| [waifu-deal-sniper](./waifu-deal-sniper) | Discord bot for anime figure collectors finding discounted pre-owned figures from AmiAmi, Mercari, and Solaris Japan |
| [wing-command](./wing-command) | Chicken wing tracker using AI-powered scraping to find the best wings near you by flavor preference |

### n8n Workflows

Pre-built n8n workflows using TinyFish — import the JSON and go.

| Workflow | Description |
|----------|-------------|
| [Competitor Scout](./N8N_WorkFlows/Competitor%20Scout%20CLI) | Research competitor feature decisions with OpenAI planning and TinyFish evidence collection |
| [Web Research Agent](./N8N_WorkFlows/Web%20Research%20Agent) | Chatbot that scrapes any website with TinyFish and saves summarized reports to Notion |
| [Daily Product Hunt Tracker](./N8N_WorkFlows/Daily%20Product%20Hunt%20Tracker) | Scheduled workflow delivering daily top 5 trending Product Hunt products to Telegram |

> More recipes added weekly!

## Getting Started with the API

You don't need to install heavy SDKs. TinyFish works with standard HTTP requests.

### 1. Get your API Key

Sign up on [tinyfish.ai](https://tinyfish.ai) and grab your API key.

### 2. Run a Command

Here is how to run a simple automation agent:

#### cURL

```bash
curl -N -X POST https://agent.tinyfish.ai/v1/automation/run-sse \
  -H "X-API-Key: $TINYFISH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://agentql.com",
    "goal": "Find all AgentQL subscription plans and their prices. Return result in json format"
  }'
```

#### Python

```python
import json
import os
import requests

response = requests.post(
    "https://agent.tinyfish.ai/v1/automation/run-sse",
    headers={
        "X-API-Key": os.getenv("TINYFISH_API_KEY"),
        "Content-Type": "application/json",
    },
    json={
        "url": "https://agentql.com",
        "goal": "Find all AgentQL subscription plans and their prices. Return result in json format",
    },
    stream=True,
)

for line in response.iter_lines():
    if line:
        line_str = line.decode("utf-8")
        if line_str.startswith("data: "):
            event = json.loads(line_str[6:])
            print(event)
```

#### TypeScript

```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://agentql.com",
    goal: "Find all AgentQL subscription plans and their prices. Return result in json format",
  }),
});

const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  console.log(decoder.decode(value));
}
```

> By the way! If you want to expose your project on localhost to your friends to show them a demo, you can now use the [tinyfi.sh](https://tinyfi.sh) by us! Completely free and easy to use!


## Star History

<p align="center">
  <a href="https://www.star-history.com/#tinyfish-io/tinyfish-cookbook&type=date">
    <img src="https://api.star-history.com/svg?repos=tinyfish-io/tinyfish-cookbook&type=date&legend=top-left" alt="Star History Chart">
  </a>
</p>

## Contributors

<a href="https://github.com/tinyfish-io/TinyFish-cookbook/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=tinyfish-io/TinyFish-cookbook" />
</a>

Got something cool you built with TinyFish? We want it in here! Check out our [Contributing Guide](CONTRIBUTING.md) for the full rundown on how to submit your project.


## Community & Support

- [Join us on Discord](https://discord.gg/tinyfish) — ask questions, share what you're building, hang out
- Learn more at [tinyfish.ai](https://tinyfish.ai)

## Legal Disclaimer

This repository is a community-driven space for sharing derivatives, code samples, and best practices related to Tiny Fish products. By using the materials in this repository, you acknowledge and agree to the following:
- **"As-Is" Basis**: All code, scripts, and documentation shared here are provided "AS IS" and "AS AVAILABLE." TinyFish makes no warranties of any kind, whether express or implied, regarding the accuracy, reliability, or security of community-contributed content.
- **No Obligation to Maintain**: Tiny Fish is under no obligation to monitor, update, or fix bugs, errors, or security vulnerabilities found in community-contributed derivatives.
- **User Responsibility**: You are solely responsible for vetting and testing any code before implementing it in a production environment. Use of these derivatives is at your own risk.
- **Limitation of Liability**: In no event shall Tiny Fish be held liable for any claim, damages, or other liability—including but not limited to system failures, data loss, or security breaches—arising from the use of or inability to use the contents of this repository.

> Note: Contributions from the community do not represent the official views or supported products of Tiny Fish.
---

<img src="https://github.com/user-attachments/assets/2cf004f0-0065-4f21-9835-12ac693964f1" width="100%" />





================================================
FILE: anime-watch-hub/.gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules

# next.js
/.next/
/out/

# production
/build

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files
.env*

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

================================================
FILE: anime-watch-hub/README.md
================================================
# Anime Watch Hub



**Live** : [https://v0-animefinder.vercel.app/](https://v0-animefinder.vercel.app/)



Anime Watch Hub helps users find exactly where a specific anime is available to stream by orchestrating intelligent platform discovery and real-time availability verification. It uses the Gemini API to identify likely streaming platforms and the TinyFish API to dispatch parallel web agents that browse those sites (Crunchyroll, Netflix, Hulu, etc.) to confirm if the title is currently in their catalog.



## Demo

https://github.com/user-attachments/assets/5425211a-43b9-40c1-b5f7-8451c7549931





## TinyFish API Usage



The application employs a two-stage process. After getting search URLs from Gemini, it calls the TinyFish SSE endpoint for each platform simultaneously to verify the anime's presence:



```typescript

const response = await fetch("https://agent.tinyfish.ai/v1/automation/run-sse", {

     method: "POST",

     headers: {

       "X-API-Key": process.env.MINO\_API\_KEY,

       "Content-Type": "application/json",

     },

     body: JSON.stringify({

       url: platform.searchUrl,

       goal: `You are checking if the anime "${animeTitle}" is available to stream on ${platformName}.



STEP 1 - HANDLE POPUPS:

Dismiss any cookie banners, login prompts, or modal dialogs.



STEP 2 - SEARCH:

If a search box is visible, search for "${animeTitle}".



STEP 3 - ANALYZE SEARCH RESULTS:

\- Check if "${animeTitle}" or a very close match appears

\- Verify it is the anime series, not related content



STEP 4 - RETURN RESULT:

{

     "available": true/false,

     "watchUrl": "URL if available",

     "message": "Brief description of what was found"

}`,

     }),

});



```



The app processes the SSE stream to show live browser status updates and provides a "Live View" link via the ```STREAMING\_URL``` event.



## How to Run

**Prerequisites**

- Node.js 18+

- A Gemini API Key

- A TinyFish API Key (\[get one here](https://accounts.mino.ai/sign-in?redirect\_url=https%3A%2F%2Fmino.ai%2Fapi-keys))



**Setup** 

1. Install dependencies:

```bash

  cd anime-watch-hub
  npm install

```

2. Configure Environment: Create a ```.env.local``` file in the root directory:

 ```bash

  GEMINI\_API\_KEY=your\_gemini\_api\_key
  MINO\_API\_KEY=your\_tinyfish\_api\_key

 ```

3. Launch Development Server:

```bash

  npm run dev

```

4. Access the App: Navigate to http://localhost:3000



## Architecture Diagram



The system follows a two-stage orchestration pattern to ensure high accuracy and real-time data:



```mermaid

graph TD

       User((User)) -->|Search Title| FE\[Next.js App]

       FE -->|Stage 1: Platform Discovery| Gemini\[Gemini API]

       Gemini -->|Returns Search URLs| FE

       

       subgraph TinyFish\_Agents \[Stage 2: Verification]

           FE -->|POST /run-sse| API\[Mino API]

           API --> A1\[Agent: Crunchyroll]

           API --> A2\[Agent: Netflix]

           API --> A3\[Agent: Hulu]

       end

       

       A1 -.->|Real-time Events| FE

       A2 -.->|Real-time Events| FE

       A3 -.->|Real-time Events| FE

       

       FE -->|Update UI| User



================================================
FILE: anime-watch-hub/app/api/check-platform/route.ts
================================================
import { NextRequest } from 'next/server'

export async function POST(request: NextRequest) {
  const encoder = new TextEncoder()

  try {
    const { animeTitle, platformName, searchUrl } = await request.json()

    if (!animeTitle || !platformName || !searchUrl) {
      return new Response(
        encoder.encode(`data: ${JSON.stringify({ type: 'ERROR', message: 'Missing required fields' })}\n\n`),
        {
          headers: {
            'Content-Type': 'text/event-stream',
            'Cache-Control': 'no-cache',
            Connection: 'keep-alive',
          },
        }
      )
    }

    const apiKey = process.env.TINYFISH_API_KEY
    if (!apiKey) {
      return new Response(
        encoder.encode(`data: ${JSON.stringify({ type: 'ERROR', message: 'Mino API key not configured' })}\n\n`),
        {
          headers: {
            'Content-Type': 'text/event-stream',
            'Cache-Control': 'no-cache',
            Connection: 'keep-alive',
          },
        }
      )
    }

    const goal = `You are checking if the anime "${animeTitle}" is available to stream on ${platformName}.

STEP 1 - HANDLE POPUPS/MODALS:
If there are any cookie consent banners, login prompts, or promotional popups, dismiss them by clicking "Accept", "Close", "X", or "Continue".

STEP 2 - SEARCH FOR THE ANIME:
The page should already be on a search results page or the search has been initiated.
If there's a search box visible, search for: "${animeTitle}"

STEP 3 - ANALYZE SEARCH RESULTS:
Look at the search results carefully:
- Check if "${animeTitle}" or a very close match appears in the results
- Look for anime thumbnails, titles, and descriptions
- Verify it's the anime series, not just related content

STEP 4 - RETURN RESULT:
Return a JSON object with these fields:
{
  "available": true/false,
  "watchUrl": "URL to watch the anime if found",
  "subscriptionRequired": true/false,
  "message": "Brief description of what you found"
}

If the anime is NOT found or not available, set available to false and explain why in the message.
If you encounter a geo-restriction or region block, mention that in the message.`

    const minoResponse = await fetch('https://agent.tinyfish.ai/v1/automation/run-sse', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': apiKey,
      },
      body: JSON.stringify({
        url: searchUrl,
        goal: goal,
      }),
    })

    if (!minoResponse.ok || !minoResponse.body) {
      return new Response(
        encoder.encode(`data: ${JSON.stringify({ type: 'ERROR', message: 'Failed to start Mino agent' })}\n\n`),
        {
          headers: {
            'Content-Type': 'text/event-stream',
            'Cache-Control': 'no-cache',
            Connection: 'keep-alive',
          },
        }
      )
    }

    // Stream the Mino response directly to the client
    const readable = new ReadableStream({
  async start(controller) {
    const decoder = new TextDecoder()

    try {
      for await (const chunk of minoResponse.body as any) {
        controller.enqueue(
          encoder.encode(decoder.decode(chunk, { stream: true }))
        )
      }
    } catch (error) {
      console.error('Error streaming Mino response:', error)
      controller.enqueue(
        encoder.encode(
          `data: ${JSON.stringify({ type: 'ERROR', message: 'Stream interrupted' })}\n\n`
        )
      )
    } finally {
      controller.close()
    }
  },
})


    return new Response(readable, {
      headers: {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        Connection: 'keep-alive',
      },
    })
  } catch (error) {
    console.error('Error in check-platform:', error)
    return new Response(
      encoder.encode(`data: ${JSON.stringify({ type: 'ERROR', message: 'Internal server error' })}\n\n`),
      {
        headers: {
          'Content-Type': 'text/event-stream',
          'Cache-Control': 'no-cache',
          Connection: 'keep-alive',
        },
      }
    )
  }
}


================================================
FILE: anime-watch-hub/app/api/discover-platforms/route.ts
================================================
import { NextRequest, NextResponse } from "next/server";

const GEMINI_MODELS = [
  "gemini-2.5-flash",
  "gemini-2.5-flash-lite",
  "gemini-2.5-pro",
];

async function callGeminiWithRetry(
  prompt: string,
  apiKey: string,
  maxRetries: number = 3
) {
  let lastError: Error | null = null;

  for (const model of GEMINI_MODELS) {
    for (let attempt = 0; attempt < maxRetries; attempt++) {
      try {
        const response = await fetch(
          `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`,
          {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify({
              contents: [
                {
                  parts: [{ text: prompt }],
                },
              ],
              generationConfig: {
                temperature: 0.2,
                maxOutputTokens: 8192,
                responseMimeType: "application/json",
              },
            }),
          }
        );

        if (response.ok) {
          return await response.json();
        }

        const errorBody = await response.text();

        // If rate limited, check if we should retry or try next model
        if (response.status === 429) {
          const retryMatch = errorBody.match(/retry in (\d+)/i);
          const retryDelay = retryMatch ? parseInt(retryMatch[1]) * 1000 : 5000;

          // If retry delay is short, wait and retry same model
          if (retryDelay <= 15000 && attempt < maxRetries - 1) {
            console.log(
              `Rate limited on ${model}, waiting ${retryDelay}ms before retry ${attempt + 1}`
            );
            await new Promise((resolve) => setTimeout(resolve, retryDelay));
            continue;
          }
          // Otherwise try next model
          console.log(`Rate limited on ${model}, trying next model...`);
          break;
        }

        lastError = new Error(`API error ${response.status}: ${errorBody}`);
      } catch (error) {
        lastError = error instanceof Error ? error : new Error(String(error));
        // Network error, wait and retry
        await new Promise((resolve) =>
          setTimeout(resolve, 1000 * (attempt + 1))
        );
      }
    }
  }

  throw lastError || new Error("All Gemini models failed");
}

export async function POST(request: NextRequest) {
  try {
    const { animeTitle } = await request.json();

    if (!animeTitle) {
      return NextResponse.json(
        { error: "Anime title is required" },
        { status: 400 }
      );
    }

    const apiKey = process.env.GEMINI_API_KEY;
    if (!apiKey) {
      return NextResponse.json(
        { error: "GEMINI_API_KEY is not configured" },
        { status: 500 }
      );
    }

    const prompt = `You are an expert at finding where anime is legally available to stream.

For the anime titled "${animeTitle}", provide a JSON array of streaming platform URLs where this specific anime might be available.

Focus on these major platforms:
- Crunchyroll (crunchyroll.com)
- Netflix (netflix.com)
- Amazon Prime Video (amazon.com/Prime-Video)
- Hulu (hulu.com)
- Funimation (funimation.com)
- HIDIVE (hidive.com)
- Disney+ (disneyplus.com)
- Max/HBO Max (max.com)

For each platform, construct the SEARCH URL where someone would search for this anime. Use the platform's search functionality.

Return ONLY a valid JSON array with this exact structure, no markdown or explanation:
[
  {
    "id": "platform-id",
    "name": "Platform Name",
    "searchUrl": "https://platform.com/search?q=anime+title"
  }
]

Examples of search URLs:
- Crunchyroll: https://www.crunchyroll.com/search?q=attack+on+titan
- Netflix: https://www.netflix.com/search?q=attack%20on%20titan
- Prime Video: https://www.amazon.com/s?k=attack+on+titan&i=instant-video
- Hulu: https://www.hulu.com/search?q=attack+on+titan

Generate search URLs for "${animeTitle}" on at least 6 platforms.`;

    const geminiData = await callGeminiWithRetry(prompt, apiKey);
    const text = geminiData.candidates?.[0]?.content?.parts?.[0]?.text;

    if (!text) {
      return NextResponse.json(
        { error: "No response from Gemini" },
        { status: 500 }
      );
    }

    // Parse JSON from response (handle potential markdown code blocks or truncation)
    let platforms;
    try {
      // First try direct parse since we requested JSON mime type
      try {
        platforms = JSON.parse(text);
      } catch {
        // Try to extract JSON array from text
        const jsonMatch = text.match(/\[[\s\S]*\]/);
        if (jsonMatch) {
          let jsonStr = jsonMatch[0];
          // Try to fix truncated JSON by closing incomplete objects/array
          if (!jsonStr.endsWith("]")) {
            // Find last complete object
            const lastCompleteIndex = jsonStr.lastIndexOf("},");
            if (lastCompleteIndex > 0) {
              jsonStr = jsonStr.substring(0, lastCompleteIndex + 1) + "]";
            }
          }
          platforms = JSON.parse(jsonStr);
        } else {
          throw new Error("No JSON array found in response");
        }
      }
      
      // Validate platforms array
      if (!Array.isArray(platforms) || platforms.length === 0) {
        throw new Error("Invalid platforms data");
      }
      
      // Filter out any incomplete platform entries
      platforms = platforms.filter(
        (p: { id?: string; name?: string; searchUrl?: string }) =>
          p && p.id && p.name && p.searchUrl && p.searchUrl.startsWith("http")
      );
      
      if (platforms.length === 0) {
        throw new Error("No valid platforms found");
      }
    } catch (parseError) {
      console.error("[v0] Failed to parse Gemini response:", text);
      console.error("[v0] Parse error:", parseError);
      return NextResponse.json(
        { error: "Failed to parse platform data" },
        { status: 500 }
      );
    }

    return NextResponse.json({ platforms });
  } catch (error) {
    console.error("Error in discover-platforms:", error);

    // Check if it's a rate limit error
    const errorMessage =
      error instanceof Error ? error.message : "Internal server error";
    if (errorMessage.includes("429") || errorMessage.includes("quota")) {
      return NextResponse.json(
        {
          error:
            "Gemini API rate limit exceeded. Please wait a minute and try again, or upgrade your API key to a paid plan.",
        },
        { status: 429 }
      );
    }

    return NextResponse.json({ error: errorMessage }, { status: 500 });
  }
}


================================================
FILE: anime-watch-hub/app/globals.css
================================================
@import 'tailwindcss';
@import 'tw-animate-css';

@custom-variant dark (&:is(.dark *));

:root {
  --background: oklch(1 0 0);
  --foreground: oklch(0.145 0 0);
  --card: oklch(1 0 0);
  --card-foreground: oklch(0.145 0 0);
  --popover: oklch(1 0 0);
  --popover-foreground: oklch(0.145 0 0);
  --primary: oklch(0.205 0 0);
  --primary-foreground: oklch(0.985 0 0);
  --secondary: oklch(0.97 0 0);
  --secondary-foreground: oklch(0.205 0 0);
  --muted: oklch(0.97 0 0);
  --muted-foreground: oklch(0.556 0 0);
  --accent: oklch(0.97 0 0);
  --accent-foreground: oklch(0.205 0 0);
  --destructive: oklch(0.577 0.245 27.325);
  --destructive-foreground: oklch(0.577 0.245 27.325);
  --border: oklch(0.922 0 0);
  --input: oklch(0.922 0 0);
  --ring: oklch(0.708 0 0);
  --chart-1: oklch(0.646 0.222 41.116);
  --chart-2: oklch(0.6 0.118 184.704);
  --chart-3: oklch(0.398 0.07 227.392);
  --chart-4: oklch(0.828 0.189 84.429);
  --chart-5: oklch(0.769 0.188 70.08);
  --radius: 0.625rem;
  --sidebar: oklch(0.985 0 0);
  --sidebar-foreground: oklch(0.145 0 0);
  --sidebar-primary: oklch(0.205 0 0);
  --sidebar-primary-foreground: oklch(0.985 0 0);
  --sidebar-accent: oklch(0.97 0 0);
  --sidebar-accent-foreground: oklch(0.205 0 0);
  --sidebar-border: oklch(0.922 0 0);
  --sidebar-ring: oklch(0.708 0 0);
}

.dark {
  --background: oklch(0.145 0 0);
  --foreground: oklch(0.985 0 0);
  --card: oklch(0.145 0 0);
  --card-foreground: oklch(0.985 0 0);
  --popover: oklch(0.145 0 0);
  --popover-foreground: oklch(0.985 0 0);
  --primary: oklch(0.985 0 0);
  --primary-foreground: oklch(0.205 0 0);
  --secondary: oklch(0.269 0 0);
  --secondary-foreground: oklch(0.985 0 0);
  --muted: oklch(0.269 0 0);
  --muted-foreground: oklch(0.708 0 0);
  --accent: oklch(0.269 0 0);
  --accent-foreground: oklch(0.985 0 0);
  --destructive: oklch(0.396 0.141 25.723);
  --destructive-foreground: oklch(0.637 0.237 25.331);
  --border: oklch(0.269 0 0);
  --input: oklch(0.269 0 0);
  --ring: oklch(0.439 0 0);
  --chart-1: oklch(0.488 0.243 264.376);
  --chart-2: oklch(0.696 0.17 162.48);
  --chart-3: oklch(0.769 0.188 70.08);
  --chart-4: oklch(0.627 0.265 303.9);
  --chart-5: oklch(0.645 0.246 16.439);
  --sidebar: oklch(0.205 0 0);
  --sidebar-foreground: oklch(0.985 0 0);
  --sidebar-primary: oklch(0.488 0.243 264.376);
  --sidebar-primary-foreground: oklch(0.985 0 0);
  --sidebar-accent: oklch(0.269 0 0);
  --sidebar-accent-foreground: oklch(0.985 0 0);
  --sidebar-border: oklch(0.269 0 0);
  --sidebar-ring: oklch(0.439 0 0);
}

@theme inline {
  --font-sans: 'Geist', 'Geist Fallback';
  --font-mono: 'Geist Mono', 'Geist Mono Fallback';
  --color-background: var(--background);
  --color-foreground: var(--foreground);
  --color-card: var(--card);
  --color-card-foreground: var(--card-foreground);
  --color-popover: var(--popover);
  --color-popover-foreground: var(--popover-foreground);
  --color-primary: var(--primary);
  --color-primary-foreground: var(--primary-foreground);
  --color-secondary: var(--secondary);
  --color-secondary-foreground: var(--secondary-foreground);
  --color-muted: var(--muted);
  --color-muted-foreground: var(--muted-foreground);
  --color-accent: var(--accent);
  --color-accent-foreground: var(--accent-foreground);
  --color-destructive: var(--destructive);
  --color-destructive-foreground: var(--destructive-foreground);
  --color-border: var(--border);
  --color-input: var(--input);
  --color-ring: var(--ring);
  --color-chart-1: var(--chart-1);
  --color-chart-2: var(--chart-2);
  --color-chart-3: var(--chart-3);
  --color-chart-4: var(--chart-4);
  --color-chart-5: var(--chart-5);
  --radius-sm: calc(var(--radius) - 4px);
  --radius-md: calc(var(--radius) - 2px);
  --radius-lg: var(--radius);
  --radius-xl: calc(var(--radius) + 4px);
  --color-sidebar: var(--sidebar);
  --color-sidebar-foreground: var(--sidebar-foreground);
  --color-sidebar-primary: var(--sidebar-primary);
  --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
  --color-sidebar-accent: var(--sidebar-accent);
  --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
  --color-sidebar-border: var(--sidebar-border);
  --color-sidebar-ring: var(--sidebar-ring);
}

@layer base {
  * {
    @apply border-border outline-ring/50;
  }
  body {
    @apply bg-background text-foreground;
  }
}


================================================
FILE: anime-watch-hub/app/layout.tsx
================================================
import React from "react"
import type { Metadata } from 'next'
import { Geist, Geist_Mono } from 'next/font/google'
import { Analytics } from '@vercel/analytics/next'
import './globals.css'

const _geist = Geist({ subsets: ["latin"] });
const _geistMono = Geist_Mono({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: 'Anime Watch Hub - Find Where to Stream Any Anime',
  description: 'Search for any anime and instantly find where it\'s available to stream across Netflix, Crunchyroll, Prime Video, Hulu, and more.',
  generator: 'v0.app',
  icons: {
    icon: [
      {
        url: '/icon-light-32x32.png',
        media: '(prefers-color-scheme: light)',
      },
      {
        url: '/icon-dark-32x32.png',
        media: '(prefers-color-scheme: dark)',
      },
      {
        url: '/icon.svg',
        type: 'image/svg+xml',
      },
    ],
    apple: '/apple-icon.png',
  },
}

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode
}>) {
  return (
    <html lang="en">
      <body className={`font-sans antialiased`}>
        {children}
        <Analytics />
      </body>
    </html>
  )
}


================================================
FILE: anime-watch-hub/app/loading.tsx
================================================
export default function Loading() {
  return null
}


================================================
FILE: anime-watch-hub/app/page.tsx
================================================
import { Suspense } from 'react'
import { AnimeWatchHub } from '@/components/anime-watch-hub'

function Loading() {
  return (
    <div className="min-h-screen bg-gradient-to-b from-background to-muted/30 flex items-center justify-center">
      <div className="animate-pulse text-muted-foreground">Loading...</div>
    </div>
  )
}

export default function Page() {
  return (
    <Suspense fallback={<Loading />}>
      <AnimeWatchHub />
    </Suspense>
  )
}


================================================
FILE: anime-watch-hub/components/anime-watch-hub.tsx
================================================
'use client'

import React from "react"
import { useSearchParams } from 'next/navigation'
import { useState } from 'react'
import { useAnimeSearch } from '@/hooks/use-anime-search'
import { PlatformCard } from '@/components/platform-card'
import { ResultsSidebar } from '@/components/results-sidebar'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Card, CardContent } from '@/components/ui/card'
import { Search, Loader2, Sparkles, RotateCcw } from 'lucide-react'

export function AnimeWatchHub() {
  const searchParams = useSearchParams()
  const [animeTitle, setAnimeTitle] = useState(searchParams?.get('title') || '')
  const [searchedTitle, setSearchedTitle] = useState('')
  const { search, reset, isSearching, isDiscovering, agents, error } = useAnimeSearch()

  const handleSearch = async (e: React.FormEvent) => {
    e.preventDefault()
    if (animeTitle.trim()) {
      setSearchedTitle(animeTitle.trim())
      await search(animeTitle.trim())
    }
  }

  const handleReset = () => {
    setAnimeTitle('')
    setSearchedTitle('')
    reset()
  }

  return (
    <div className="min-h-screen bg-gradient-to-b from-background to-muted/30">
      {/* Header */}
      <header className="border-b bg-background/80 backdrop-blur-sm sticky top-0 z-10">
        <div className="container mx-auto px-4 py-4">
          <div className="flex items-center justify-between">
            <div className="flex items-center gap-3">
              <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary text-primary-foreground">
                <Sparkles className="h-5 w-5" />
              </div>
              <div>
                <h1 className="text-xl font-bold">Anime Watch Hub</h1>
                <p className="text-xs text-muted-foreground">Find where to stream any anime</p>
              </div>
            </div>
            {agents.length > 0 && (
              <Button variant="outline" size="sm" onClick={handleReset}>
                <RotateCcw className="mr-2 h-4 w-4" />
                New Search
              </Button>
            )}
          </div>
        </div>
      </header>

      <main className="container mx-auto px-4 py-8">
        {/* Search Section */}
        <div className="mx-auto max-w-2xl">
          <Card className="border-2">
            <CardContent className="pt-6">
              <form onSubmit={handleSearch} className="space-y-4">
                <div className="space-y-2">
                  <label htmlFor="anime-search" className="text-sm font-medium">
                    Search for an anime
                  </label>
                  <div className="flex gap-2">
                    <Input
                      id="anime-search"
                      type="text"
                      placeholder="e.g., Attack on Titan, Demon Slayer, One Piece..."
                      value={animeTitle}
                      onChange={(e) => setAnimeTitle(e.target.value)}
                      disabled={isSearching}
                      className="text-base"
                    />
                    <Button type="submit" disabled={isSearching || !animeTitle.trim()}>
                      {isSearching ? (
                        <Loader2 className="h-4 w-4 animate-spin" />
                      ) : (
                        <Search className="h-4 w-4" />
                      )}
                      <span className="ml-2 hidden sm:inline">Search</span>
                    </Button>
                  </div>
                </div>
                {error && (
                  <p className="text-sm text-destructive">{error}</p>
                )}
              </form>
            </CardContent>
          </Card>
        </div>

        {/* Discovery Status */}
        {isDiscovering && (
          <div className="mx-auto mt-8 max-w-2xl">
            <Card>
              <CardContent className="flex items-center justify-center gap-3 py-8">
                <Loader2 className="h-5 w-5 animate-spin text-primary" />
                <p className="text-muted-foreground">
               
Download .txt
gitextract_gre32cio/

├── .github/
│   ├── CODEOWNERS
│   ├── config/
│   │   └── .pre-commit-config-template.yaml
│   └── workflows/
│       ├── secrets-scanner.yml
│       └── vuln-scanner-pr.yml
├── .semgrepignore
├── .tags.json
├── .yamllint
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── Manga-Availability-Finder/
│   ├── README.md
│   ├── docs/
│   │   └── MINO_API_INTEGRATION.md
│   ├── package.json
│   ├── public/
│   │   └── robots.txt
│   ├── src/
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── AgentCard.tsx
│   │   │   ├── NavLink.tsx
│   │   │   ├── ResultsSummary.tsx
│   │   │   ├── SearchHero.tsx
│   │   │   └── ui/
│   │   │       ├── avatar.tsx
│   │   │       ├── button.tsx
│   │   │       └── card.tsx
│   │   ├── hooks/
│   │   │   └── useMangaSearch.ts
│   │   ├── integrations/
│   │   │   └── supabase/
│   │   │       ├── client.ts
│   │   │       └── types.ts
│   │   ├── lib/
│   │   │   └── utils.ts
│   │   └── pages/
│   │       ├── Index.tsx
│   │       └── NotFound.tsx
│   ├── supabase/
│   │   ├── config.toml
│   │   └── functions/
│   │       ├── discover-manga-sites/
│   │       │   └── index.ts
│   │       └── search-manga/
│   │           └── index.ts
│   └── tsconfig.json
├── N8N_WorkFlows/
│   ├── Competitor Scout CLI/
│   │   ├── Competitor Scout via Tinyfish.json
│   │   └── README.md
│   ├── Daily Product Hunt Tracker/
│   │   ├── Daily Product Hunt Tracker via Tinyfish.json
│   │   └── README.md
│   ├── Web Research Agent/
│   │   ├── README.md
│   │   └── Web Research Agent via Tinyfish.json
│   └── ai-competitor-analysis/
│       ├── README.md
│       └── ai-competitor-radar.json
├── README.md
├── anime-watch-hub/
│   ├── .gitignore
│   ├── README.md
│   ├── app/
│   │   ├── api/
│   │   │   ├── check-platform/
│   │   │   │   └── route.ts
│   │   │   └── discover-platforms/
│   │   │       └── route.ts
│   │   ├── globals.css
│   │   ├── layout.tsx
│   │   ├── loading.tsx
│   │   └── page.tsx
│   ├── components/
│   │   ├── anime-watch-hub.tsx
│   │   ├── platform-card.tsx
│   │   ├── results-sidebar.tsx
│   │   ├── theme-provider.tsx
│   │   └── ui/
│   │       ├── accordion.tsx
│   │       ├── alert-dialog.tsx
│   │       ├── alert.tsx
│   │       ├── aspect-ratio.tsx
│   │       ├── avatar.tsx
│   │       ├── badge.tsx
│   │       ├── breadcrumb.tsx
│   │       ├── button-group.tsx
│   │       ├── button.tsx
│   │       ├── calendar.tsx
│   │       ├── card.tsx
│   │       ├── carousel.tsx
│   │       ├── chart.tsx
│   │       ├── checkbox.tsx
│   │       ├── collapsible.tsx
│   │       ├── command.tsx
│   │       ├── context-menu.tsx
│   │       ├── dialog.tsx
│   │       ├── drawer.tsx
│   │       ├── dropdown-menu.tsx
│   │       ├── empty.tsx
│   │       ├── field.tsx
│   │       ├── form.tsx
│   │       ├── hover-card.tsx
│   │       ├── input-group.tsx
│   │       ├── input-otp.tsx
│   │       ├── input.tsx
│   │       ├── item.tsx
│   │       ├── kbd.tsx
│   │       ├── label.tsx
│   │       ├── menubar.tsx
│   │       ├── navigation-menu.tsx
│   │       ├── pagination.tsx
│   │       ├── popover.tsx
│   │       ├── progress.tsx
│   │       ├── radio-group.tsx
│   │       ├── resizable.tsx
│   │       ├── scroll-area.tsx
│   │       ├── select.tsx
│   │       ├── separator.tsx
│   │       ├── sheet.tsx
│   │       ├── sidebar.tsx
│   │       ├── skeleton.tsx
│   │       ├── slider.tsx
│   │       ├── sonner.tsx
│   │       ├── spinner.tsx
│   │       ├── switch.tsx
│   │       ├── table.tsx
│   │       ├── tabs.tsx
│   │       ├── textarea.tsx
│   │       ├── toast.tsx
│   │       ├── toaster.tsx
│   │       ├── toggle-group.tsx
│   │       ├── toggle.tsx
│   │       ├── tooltip.tsx
│   │       ├── use-mobile.tsx
│   │       └── use-toast.ts
│   ├── components.json
│   ├── docs/
│   │   └── mino-api-integration.md
│   ├── hooks/
│   │   ├── use-anime-search.ts
│   │   ├── use-mobile.ts
│   │   └── use-toast.ts
│   ├── lib/
│   │   ├── types.ts
│   │   └── utils.ts
│   ├── next.config.mjs
│   ├── package.json
│   ├── postcss.config.mjs
│   ├── styles/
│   │   └── globals.css
│   └── tsconfig.json
├── bestbet/
│   ├── .gitignore
│   ├── README.md
│   ├── app/
│   │   ├── components/
│   │   │   ├── MoneyParticle.tsx
│   │   │   └── SportsbookSelector.tsx
│   │   ├── globals.css
│   │   ├── layout.tsx
│   │   ├── page.tsx
│   │   └── webagent.ts
│   ├── eslint.config.mjs
│   ├── next.config.ts
│   ├── package.json
│   ├── postcss.config.mjs
│   └── tsconfig.json
├── code-reference-finder/
│   ├── .gitignore
│   ├── README.md
│   ├── extension/
│   │   ├── background.js
│   │   ├── manifest.json
│   │   ├── sidepanel.html
│   │   └── sidepanel.js
│   ├── next.config.ts
│   ├── package.json
│   ├── postcss.config.mjs
│   ├── src/
│   │   ├── app/
│   │   │   ├── api/
│   │   │   │   └── analyze/
│   │   │   │       └── route.ts
│   │   │   ├── globals.css
│   │   │   ├── layout.tsx
│   │   │   └── page.tsx
│   │   ├── components/
│   │   │   ├── AgentCard.tsx
│   │   │   ├── AnalysisSummary.tsx
│   │   │   ├── CodeInput.tsx
│   │   │   ├── Dashboard.tsx
│   │   │   ├── Header.tsx
│   │   │   ├── LiveBrowserPreview.tsx
│   │   │   ├── PipelineProgress.tsx
│   │   │   ├── ReferenceCard.tsx
│   │   │   └── ReferenceGrid.tsx
│   │   ├── context/
│   │   │   └── AppContext.tsx
│   │   ├── hooks/
│   │   │   └── useCodeAnalysis.ts
│   │   └── lib/
│   │       ├── constants.ts
│   │       ├── goal-builder.ts
│   │       ├── mino-client.ts
│   │       ├── openrouter.ts
│   │       ├── orchestrator.ts
│   │       ├── search.ts
│   │       └── types.ts
│   └── tsconfig.json
├── competitor-analysis/
│   ├── .gitignore
│   ├── .mcp.json
│   ├── CHANGELOG.md
│   ├── FEATURES.md
│   ├── MINO_API_FEEDBACK.md
│   ├── README.md
│   ├── app/
│   │   ├── analysis/
│   │   │   └── page.tsx
│   │   ├── api/
│   │   │   ├── analyze-pricing/
│   │   │   │   └── route.ts
│   │   │   ├── generate-urls/
│   │   │   │   └── route.ts
│   │   │   └── scrape-pricing/
│   │   │       └── route.ts
│   │   ├── company/
│   │   │   └── [id]/
│   │   │       └── page.tsx
│   │   ├── competitors/
│   │   │   └── page.tsx
│   │   ├── dashboard/
│   │   │   └── page.tsx
│   │   ├── globals.css
│   │   ├── layout.tsx
│   │   └── page.tsx
│   ├── components/
│   │   ├── competitor-input.tsx
│   │   ├── dashboard-layout.tsx
│   │   ├── settings-panel.tsx
│   │   ├── spreadsheet-view.tsx
│   │   └── ui/
│   │       ├── badge.tsx
│   │       ├── button.tsx
│   │       ├── card.tsx
│   │       ├── dialog.tsx
│   │       ├── dot-pattern.tsx
│   │       ├── input.tsx
│   │       ├── select.tsx
│   │       ├── separator.tsx
│   │       ├── sheet.tsx
│   │       ├── sidebar.tsx
│   │       ├── skeleton.tsx
│   │       ├── table.tsx
│   │       ├── tabs.tsx
│   │       ├── textarea.tsx
│   │       └── tooltip.tsx
│   ├── components.json
│   ├── eslint.config.mjs
│   ├── hooks/
│   │   └── use-mobile.ts
│   ├── lib/
│   │   ├── ai-client.ts
│   │   ├── ai-schemas.ts
│   │   ├── mino-client.ts
│   │   ├── pricing-context.tsx
│   │   └── utils.ts
│   ├── next.config.ts
│   ├── package.json
│   ├── postcss.config.mjs
│   ├── tsconfig.json
│   └── types/
│       └── index.ts
├── competitor-scout-cli/
│   ├── .gitignore
│   ├── .vscode/
│   │   └── settings.json
│   ├── FILE_ARCHITECTURE.md
│   ├── PRODUCT.md
│   ├── README.md
│   ├── app/
│   │   ├── api/
│   │   │   └── research/
│   │   │       └── route.ts
│   │   ├── globals.css
│   │   ├── layout.tsx
│   │   └── page.tsx
│   ├── cli/
│   │   └── scout.mjs
│   ├── components/
│   │   ├── cli-preview.tsx
│   │   ├── competitor-panel.tsx
│   │   ├── event-log.tsx
│   │   ├── query-input.tsx
│   │   └── report-view.tsx
│   ├── lib/
│   │   ├── env.ts
│   │   ├── openai-client.ts
│   │   ├── tinyfish.ts
│   │   ├── types.ts
│   │   └── utils.ts
│   ├── next-env.d.ts
│   ├── next.config.mjs
│   ├── package.json
│   ├── postcss.config.mjs
│   └── tsconfig.json
├── concept-discovery-system/
│   ├── .gitignore
│   ├── README.md
│   ├── eslint.config.js
│   ├── index.html
│   ├── package.json
│   ├── src/
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── ErrorBoundary.tsx
│   │   │   ├── input/
│   │   │   │   └── InputForm.tsx
│   │   │   ├── layout/
│   │   │   │   ├── Footer.tsx
│   │   │   │   └── Header.tsx
│   │   │   └── results/
│   │   │       ├── AnalysisPanel.tsx
│   │   │       ├── ConceptCard.tsx
│   │   │       ├── ConceptCardLoading.tsx
│   │   │       ├── Dashboard.tsx
│   │   │       ├── DetailPanel.tsx
│   │   │       └── ResultsGrid.tsx
│   │   ├── context/
│   │   │   └── DiscoveryContext.tsx
│   │   ├── hooks/
│   │   │   └── useConceptDiscovery.ts
│   │   ├── index.css
│   │   ├── lib/
│   │   │   ├── constants.ts
│   │   │   ├── goal-builder.ts
│   │   │   ├── openrouter-client.ts
│   │   │   ├── query-generator.ts
│   │   │   ├── search-engines.ts
│   │   │   ├── tinyfish-client.ts
│   │   │   └── utils.ts
│   │   ├── main.tsx
│   │   └── types/
│   │       └── index.ts
│   ├── tsconfig.app.json
│   ├── tsconfig.json
│   ├── tsconfig.node.json
│   ├── vercel.json
│   └── vite.config.ts
├── fast-qa/
│   ├── .gitignore
│   ├── README.md
│   ├── app/
│   │   ├── api/
│   │   │   ├── execute-tests/
│   │   │   │   └── route.ts
│   │   │   ├── generate-report/
│   │   │   │   └── route.ts
│   │   │   ├── generate-tests/
│   │   │   │   └── route.ts
│   │   │   └── parse-test/
│   │   │       └── route.ts
│   │   ├── globals.css
│   │   ├── layout.tsx
│   │   └── page.tsx
│   ├── components/
│   │   ├── qa/
│   │   │   ├── ai-test-generator.tsx
│   │   │   ├── dashboard-layout.tsx
│   │   │   ├── index.ts
│   │   │   ├── project-card.tsx
│   │   │   ├── project-dialog.tsx
│   │   │   ├── settings-panel.tsx
│   │   │   ├── test-case-detail.tsx
│   │   │   ├── test-case-editor.tsx
│   │   │   ├── test-case-list.tsx
│   │   │   ├── test-execution-grid.tsx
│   │   │   └── test-results-table.tsx
│   │   └── ui/
│   │       ├── accordion.tsx
│   │       ├── alert-dialog.tsx
│   │       ├── alert.tsx
│   │       ├── badge.tsx
│   │       ├── button.tsx
│   │       ├── card.tsx
│   │       ├── checkbox.tsx
│   │       ├── collapsible.tsx
│   │       ├── dialog.tsx
│   │       ├── dropdown-menu.tsx
│   │       ├── form.tsx
│   │       ├── input.tsx
│   │       ├── label.tsx
│   │       ├── progress.tsx
│   │       ├── select.tsx
│   │       ├── separator.tsx
│   │       ├── sheet.tsx
│   │       ├── skeleton.tsx
│   │       ├── switch.tsx
│   │       ├── table.tsx
│   │       ├── tabs.tsx
│   │       ├── textarea.tsx
│   │       └── tooltip.tsx
│   ├── components.json
│   ├── eslint.config.mjs
│   ├── lib/
│   │   ├── ai-client.ts
│   │   ├── hooks.ts
│   │   ├── mino-client.ts
│   │   ├── qa-context.tsx
│   │   └── utils.ts
│   ├── next.config.ts
│   ├── package.json
│   ├── postcss.config.mjs
│   ├── tsconfig.json
│   └── types/
│       └── index.ts
├── game-buying-guide/
│   ├── .gitignore
│   ├── README.md
│   ├── app/
│   │   ├── api/
│   │   │   ├── analyze-platform/
│   │   │   │   └── route.ts
│   │   │   ├── discover-platforms/
│   │   │   │   └── route.ts
│   │   │   └── steamdb-price-history/
│   │   │       └── route.ts
│   │   ├── globals.css
│   │   ├── layout.tsx
│   │   ├── loading.tsx
│   │   └── page.tsx
│   ├── components/
│   │   ├── agent-card.tsx
│   │   ├── agent-grid.tsx
│   │   ├── live-browser-preview.tsx
│   │   ├── results-summary.tsx
│   │   ├── search-form.tsx
│   │   ├── steamdb-price-card.tsx
│   │   ├── theme-provider.tsx
│   │   └── ui/
│   │       ├── accordion.tsx
│   │       ├── alert-dialog.tsx
│   │       ├── alert.tsx
│   │       ├── aspect-ratio.tsx
│   │       ├── avatar.tsx
│   │       ├── badge.tsx
│   │       ├── breadcrumb.tsx
│   │       ├── button-group.tsx
│   │       ├── button.tsx
│   │       ├── calendar.tsx
│   │       ├── card.tsx
│   │       ├── carousel.tsx
│   │       ├── chart.tsx
│   │       ├── checkbox.tsx
│   │       ├── collapsible.tsx
│   │       ├── command.tsx
│   │       ├── context-menu.tsx
│   │       ├── dialog.tsx
│   │       ├── drawer.tsx
│   │       ├── dropdown-menu.tsx
│   │       ├── empty.tsx
│   │       ├── field.tsx
│   │       ├── form.tsx
│   │       ├── hover-card.tsx
│   │       ├── input-group.tsx
│   │       ├── input-otp.tsx
│   │       ├── input.tsx
│   │       ├── item.tsx
│   │       ├── kbd.tsx
│   │       ├── label.tsx
│   │       ├── menubar.tsx
│   │       ├── navigation-menu.tsx
│   │       ├── pagination.tsx
│   │       ├── popover.tsx
│   │       ├── progress.tsx
│   │       ├── radio-group.tsx
│   │       ├── resizable.tsx
│   │       ├── scroll-area.tsx
│   │       ├── select.tsx
│   │       ├── separator.tsx
│   │       ├── sheet.tsx
│   │       ├── sidebar.tsx
│   │       ├── skeleton.tsx
│   │       ├── slider.tsx
│   │       ├── sonner.tsx
│   │       ├── spinner.tsx
│   │       ├── switch.tsx
│   │       ├── table.tsx
│   │       ├── tabs.tsx
│   │       ├── textarea.tsx
│   │       ├── toast.tsx
│   │       ├── toaster.tsx
│   │       ├── toggle-group.tsx
│   │       ├── toggle.tsx
│   │       ├── tooltip.tsx
│   │       ├── use-mobile.tsx
│   │       └── use-toast.ts
│   ├── components.json
│   ├── docs/
│   │   └── mino-api-integration.md
│   ├── hooks/
│   │   ├── use-game-search.ts
│   │   ├── use-mobile.ts
│   │   └── use-toast.ts
│   ├── lib/
│   │   ├── types.ts
│   │   └── utils.ts
│   ├── next.config.mjs
│   ├── package.json
│   ├── postcss.config.mjs
│   ├── styles/
│   │   └── globals.css
│   └── tsconfig.json
├── golden-images.yaml
├── lego-hunter/
│   ├── .env.example
│   ├── .gitignore
│   ├── README.md
│   ├── app/
│   │   ├── api/
│   │   │   ├── generate-urls/
│   │   │   │   └── route.ts
│   │   │   └── search-lego/
│   │   │       └── route.ts
│   │   ├── globals.css
│   │   ├── layout.tsx
│   │   └── page.tsx
│   ├── components/
│   │   ├── best-deal-card.tsx
│   │   ├── browser-preview.tsx
│   │   ├── lego-confetti.tsx
│   │   ├── results-table.tsx
│   │   ├── retailer-card.tsx
│   │   └── ui/
│   │       ├── button.tsx
│   │       ├── card.tsx
│   │       └── input.tsx
│   ├── components.json
│   ├── eslint.config.mjs
│   ├── lib/
│   │   ├── gemini-client.ts
│   │   ├── retailers.ts
│   │   └── utils.ts
│   ├── next.config.ts
│   ├── package.json
│   ├── postcss.config.mjs
│   ├── tsconfig.json
│   └── types/
│       └── index.ts
├── loan-decision-copilot/
│   ├── README.md
│   ├── docs/
│   │   └── MINO_API_DOCUMENTATION (1).md
│   ├── package.json
│   ├── public/
│   │   └── robots.txt
│   ├── src/
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── AgentCard.tsx
│   │   │   ├── BankDetailPanel.tsx
│   │   │   ├── LiveBrowserPreview.tsx
│   │   │   ├── LoanTypeSelector.tsx
│   │   │   ├── LocationInput.tsx
│   │   │   ├── NavLink.tsx
│   │   │   ├── SearchProgress.tsx
│   │   │   └── ui/
│   │   │       ├── avatar.tsx
│   │   │       ├── button.tsx
│   │   │       └── card.tsx
│   │   ├── hooks/
│   │   │   └── useLoanSearch.ts
│   │   ├── integrations/
│   │   │   └── supabase/
│   │   │       ├── client.ts
│   │   │       └── types.ts
│   │   ├── lib/
│   │   │   └── utils.ts
│   │   ├── pages/
│   │   │   ├── Index.tsx
│   │   │   └── NotFound.tsx
│   │   └── types/
│   │       └── loan.ts
│   ├── supabase/
│   │   ├── config.toml
│   │   └── functions/
│   │       ├── analyze-loan/
│   │       │   └── index.ts
│   │       └── discover-banks/
│   │           └── index.ts
│   └── tsconfig.json
├── logistics-sentry/
│   ├── README.md
│   ├── jsconfig.json
│   ├── next.config.js
│   ├── package.json
│   ├── postcss.config.js
│   ├── src/
│   │   ├── app/
│   │   │   ├── api/
│   │   │   │   ├── agent/
│   │   │   │   │   └── run/
│   │   │   │   │       └── route.js
│   │   │   │   ├── logistics/
│   │   │   │   │   └── risk-assessment/
│   │   │   │   │       └── route.js
│   │   │   │   └── pricing/
│   │   │   │       └── run/
│   │   │   │           └── route.js
│   │   │   ├── competitive-pricing/
│   │   │   │   └── page.js
│   │   │   ├── globals.css
│   │   │   ├── layout.js
│   │   │   └── page.js
│   │   ├── components/
│   │   │   ├── ActionPanel.js
│   │   │   ├── ActivityFeed.js
│   │   │   ├── AgentHeader.js
│   │   │   ├── DecisionReasoning.js
│   │   │   ├── InventoryAlert.js
│   │   │   ├── InventoryInput.js
│   │   │   ├── LiveStream.js
│   │   │   ├── MetricCard.js
│   │   │   ├── RiskAssessment.js
│   │   │   └── TinyFishAgentAesthetics.js
│   │   ├── hooks/
│   │   │   └── use-toast.js
│   │   └── lib/
│   │       ├── decision-engine.js
│   │       ├── logistics/
│   │       │   └── agent.js
│   │       ├── pricing-intelligence.js
│   │       ├── tinyfish.js
│   │       └── utils.js
│   └── tailwind.config.js
├── openbox-deals/
│   ├── .gitignore
│   ├── README.md
│   ├── app/
│   │   ├── __init__.py
│   │   └── main.py
│   ├── railway.toml
│   ├── requirements.txt
│   └── static/
│       └── index.html
├── renovate.json
├── research-sentry/
│   ├── .gitignore
│   ├── README.md
│   ├── app/
│   │   ├── api/
│   │   │   ├── citations/
│   │   │   │   └── track/
│   │   │   │       └── route.ts
│   │   │   ├── compare/
│   │   │   │   └── route.ts
│   │   │   ├── conversation/
│   │   │   │   └── route.ts
│   │   │   ├── emails/
│   │   │   │   └── extract/
│   │   │   │       └── route.ts
│   │   │   ├── export/
│   │   │   │   └── bibtex/
│   │   │   │       └── route.ts
│   │   │   ├── health/
│   │   │   │   └── route.ts
│   │   │   ├── search/
│   │   │   │   ├── text/
│   │   │   │   │   └── route.ts
│   │   │   │   └── voice/
│   │   │   │       └── route.ts
│   │   │   └── summarize/
│   │   │       └── route.ts
│   │   ├── globals.css
│   │   ├── layout.tsx
│   │   └── page.tsx
│   ├── components/
│   │   ├── AudioPlayer.tsx
│   │   ├── CitationTracker.tsx
│   │   ├── CoPilotMode.tsx
│   │   ├── ConversationInterface.tsx
│   │   ├── ErrorMessage.tsx
│   │   ├── LoadingSpinner.tsx
│   │   ├── PaperCard.tsx
│   │   ├── PaperComparison.tsx
│   │   ├── PaperSummary.tsx
│   │   ├── ResultsGrid.tsx
│   │   ├── SearchInterface.tsx
│   │   ├── TinyFishAgentTerminal.tsx
│   │   ├── VoiceRecorder.tsx
│   │   └── WorkflowSelector.tsx
│   ├── hooks/
│   │   └── useVoiceCommands.ts
│   ├── lib/
│   │   ├── aggregator.ts
│   │   ├── audio-utils.ts
│   │   ├── citation-tracker.ts
│   │   ├── comparator.ts
│   │   ├── conversation.ts
│   │   ├── email-utils.ts
│   │   ├── intent-parser.ts
│   │   ├── mino.ts
│   │   ├── pdf-utils.ts
│   │   ├── search.ts
│   │   ├── summarizer.ts
│   │   ├── types.ts
│   │   ├── whisper.ts
│   │   └── workflows.ts
│   ├── next-env.d.ts
│   ├── next.config.js
│   ├── package.json
│   ├── postcss.config.js
│   ├── tailwind.config.ts
│   ├── tsconfig.json
│   ├── vercel.json
│   └── voice-research-project.txt
├── restaurant-comparison-tool/
│   ├── .gitignore
│   ├── README.md
│   ├── components.json
│   ├── eslint.config.js
│   ├── index.html
│   ├── package.json
│   ├── src/
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── layout/
│   │   │   │   ├── Footer.tsx
│   │   │   │   └── Header.tsx
│   │   │   ├── live/
│   │   │   │   ├── AgentStatusIndicator.tsx
│   │   │   │   ├── LiveBrowserPreview.tsx
│   │   │   │   ├── LiveSearchPanel.tsx
│   │   │   │   └── RestaurantAgentCard.tsx
│   │   │   ├── results/
│   │   │   │   ├── AgentLoadingCard.tsx
│   │   │   │   ├── AllergenRiskBadge.tsx
│   │   │   │   ├── AllergenRiskPanel.tsx
│   │   │   │   ├── ComparisonDashboard.tsx
│   │   │   │   ├── ConfidenceIndicator.tsx
│   │   │   │   ├── FitExplanation.tsx
│   │   │   │   ├── GoogleMapsLink.tsx
│   │   │   │   ├── RestaurantResultCard.tsx
│   │   │   │   ├── ResultDetailPanel.tsx
│   │   │   │   └── SafetyScoreRing.tsx
│   │   │   ├── search/
│   │   │   │   ├── AllergenSelector.tsx
│   │   │   │   ├── PreferenceSelector.tsx
│   │   │   │   ├── RestaurantInput.tsx
│   │   │   │   └── SearchForm.tsx
│   │   │   └── ui/
│   │   │       ├── badge.tsx
│   │   │       ├── button.tsx
│   │   │       ├── card.tsx
│   │   │       ├── dialog.tsx
│   │   │       ├── input.tsx
│   │   │       ├── scroll-area.tsx
│   │   │       ├── select.tsx
│   │   │       ├── separator.tsx
│   │   │       ├── skeleton.tsx
│   │   │       ├── toggle.tsx
│   │   │       └── tooltip.tsx
│   │   ├── context/
│   │   │   └── SearchContext.tsx
│   │   ├── hooks/
│   │   │   └── useRestaurantSearch.ts
│   │   ├── index.css
│   │   ├── lib/
│   │   │   ├── allergens.ts
│   │   │   ├── constants.ts
│   │   │   ├── goal-builder.ts
│   │   │   ├── score-calculator.ts
│   │   │   ├── tinyfish-client.ts
│   │   │   └── utils.ts
│   │   ├── main.tsx
│   │   └── types/
│   │       └── index.ts
│   ├── tsconfig.app.json
│   ├── tsconfig.json
│   ├── tsconfig.node.json
│   └── vite.config.ts
├── scholarship-finder/
│   ├── .env.example
│   ├── .gitignore
│   ├── README.md
│   ├── bun.lockb
│   ├── components.json
│   ├── eslint.config.js
│   ├── index.html
│   ├── package.json
│   ├── postcss.config.js
│   ├── public/
│   │   └── robots.txt
│   ├── src/
│   │   ├── App.css
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── CompareButton.tsx
│   │   │   ├── CompareDashboard.tsx
│   │   │   ├── Header.tsx
│   │   │   ├── LoadingAnimation.tsx
│   │   │   ├── NavLink.tsx
│   │   │   ├── ScholarshipCard.tsx
│   │   │   ├── SearchForm.tsx
│   │   │   ├── SearchResults.tsx
│   │   │   ├── SelectableScholarshipCard.tsx
│   │   │   └── ui/
│   │   │       ├── accordion.tsx
│   │   │       ├── alert-dialog.tsx
│   │   │       ├── alert.tsx
│   │   │       ├── aspect-ratio.tsx
│   │   │       ├── avatar.tsx
│   │   │       ├── badge.tsx
│   │   │       ├── breadcrumb.tsx
│   │   │       ├── button.tsx
│   │   │       ├── calendar.tsx
│   │   │       ├── card.tsx
│   │   │       ├── carousel.tsx
│   │   │       ├── chart.tsx
│   │   │       ├── checkbox.tsx
│   │   │       ├── collapsible.tsx
│   │   │       ├── command.tsx
│   │   │       ├── context-menu.tsx
│   │   │       ├── dialog.tsx
│   │   │       ├── drawer.tsx
│   │   │       ├── dropdown-menu.tsx
│   │   │       ├── form.tsx
│   │   │       ├── hover-card.tsx
│   │   │       ├── input-otp.tsx
│   │   │       ├── input.tsx
│   │   │       ├── label.tsx
│   │   │       ├── menubar.tsx
│   │   │       ├── navigation-menu.tsx
│   │   │       ├── pagination.tsx
│   │   │       ├── popover.tsx
│   │   │       ├── progress.tsx
│   │   │       ├── radio-group.tsx
│   │   │       ├── resizable.tsx
│   │   │       ├── scroll-area.tsx
│   │   │       ├── select.tsx
│   │   │       ├── separator.tsx
│   │   │       ├── sheet.tsx
│   │   │       ├── sidebar.tsx
│   │   │       ├── skeleton.tsx
│   │   │       ├── slider.tsx
│   │   │       ├── sonner.tsx
│   │   │       ├── switch.tsx
│   │   │       ├── table.tsx
│   │   │       ├── tabs.tsx
│   │   │       ├── textarea.tsx
│   │   │       ├── toast.tsx
│   │   │       ├── toaster.tsx
│   │   │       ├── toggle-group.tsx
│   │   │       ├── toggle.tsx
│   │   │       ├── tooltip.tsx
│   │   │       └── use-toast.ts
│   │   ├── hooks/
│   │   │   ├── use-mobile.tsx
│   │   │   ├── use-toast.ts
│   │   │   └── useScholarshipSearch.ts
│   │   ├── index.css
│   │   ├── integrations/
│   │   │   └── supabase/
│   │   │       ├── client.ts
│   │   │       └── types.ts
│   │   ├── lib/
│   │   │   └── utils.ts
│   │   ├── main.tsx
│   │   ├── pages/
│   │   │   ├── Index.tsx
│   │   │   └── NotFound.tsx
│   │   ├── test/
│   │   │   ├── example.test.ts
│   │   │   └── setup.ts
│   │   ├── types/
│   │   │   └── scholarship.ts
│   │   └── vite-env.d.ts
│   ├── supabase/
│   │   ├── config.toml
│   │   └── functions/
│   │       └── search-scholarships/
│   │           └── index.ts
│   ├── tailwind.config.ts
│   ├── tsconfig.app.json
│   ├── tsconfig.json
│   ├── tsconfig.node.json
│   ├── vite.config.ts
│   └── vitest.config.ts
├── silicon-signal/
│   ├── .env.example
│   ├── .gitignore
│   ├── README.md
│   ├── data/
│   │   └── history.json
│   ├── eslint.config.mjs
│   ├── next.config.ts
│   ├── package.json
│   ├── postcss.config.mjs
│   ├── src/
│   │   ├── app/
│   │   │   ├── api/
│   │   │   │   └── scan/
│   │   │   │       └── route.ts
│   │   │   ├── globals.css
│   │   │   ├── layout.tsx
│   │   │   └── page.tsx
│   │   ├── components/
│   │   │   ├── Dashboard.tsx
│   │   │   ├── Header.tsx
│   │   │   ├── HistoricalTrend.tsx
│   │   │   ├── PlatformView.tsx
│   │   │   ├── RiskBadge.tsx
│   │   │   ├── ScanForm.tsx
│   │   │   ├── ScanResultCard.tsx
│   │   │   ├── SignalOverview.tsx
│   │   │   ├── SiliconWafer.tsx
│   │   │   ├── SystemArchitecture.tsx
│   │   │   └── VendorIntelligence.tsx
│   │   ├── lib/
│   │   │   └── store.ts
│   │   └── types.ts
│   └── tsconfig.json
├── skills/
│   └── use-tinyfish/
│       └── SKILL.md
├── stay-scout-hub/
│   ├── README.md
│   ├── docs/
│   │   └── MINO_AREA_RESEARCH_API.md
│   ├── package.json
│   ├── public/
│   │   └── robots.txt
│   ├── src/
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── AreaCard.tsx
│   │   │   ├── AreaResultsSection.tsx
│   │   │   ├── HeroSection.tsx
│   │   │   ├── LiveBrowserPreview.tsx
│   │   │   ├── NavLink.tsx
│   │   │   ├── PlatformCard.tsx
│   │   │   ├── PurposeSelector.tsx
│   │   │   ├── ResultsSection.tsx
│   │   │   ├── SearchFormV2.tsx
│   │   │   └── ui/
│   │   │       ├── button.tsx
│   │   │       └── card.tsx
│   │   ├── hooks/
│   │   │   ├── useAreaSearch.ts
│   │   │   └── useHotelSearch.ts
│   │   ├── integrations/
│   │   │   └── supabase/
│   │   │       ├── client.ts
│   │   │       └── types.ts
│   │   ├── lib/
│   │   │   ├── api/
│   │   │   │   ├── area-search.ts
│   │   │   │   └── hotel-search.ts
│   │   │   └── utils.ts
│   │   ├── pages/
│   │   │   ├── Index.tsx
│   │   │   └── NotFound.tsx
│   │   └── types/
│   │       └── hotel.ts
│   ├── supabase/
│   │   ├── config.toml
│   │   └── functions/
│   │       ├── check-platform/
│   │       │   └── index.ts
│   │       ├── discover-areas/
│   │       │   └── index.ts
│   │       ├── discover-platforms/
│   │       │   └── index.ts
│   │       └── reasearch-area/
│   │           └── index.ts
│   └── tsconfig.json
├── summer-school-finder/
│   ├── README.md
│   ├── bun.lockb
│   ├── components.json
│   ├── eslint.config.js
│   ├── index.html
│   ├── package.json
│   ├── postcss.config.js
│   ├── public/
│   │   └── robots.txt
│   ├── src/
│   │   ├── App.css
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── AgentCard.tsx
│   │   │   ├── CompareModal.tsx
│   │   │   ├── LiveAgentCard.tsx
│   │   │   ├── NavLink.tsx
│   │   │   ├── ResultCard.tsx
│   │   │   ├── SearchForm.tsx
│   │   │   └── ui/
│   │   │       ├── accordion.tsx
│   │   │       ├── alert-dialog.tsx
│   │   │       ├── alert.tsx
│   │   │       ├── aspect-ratio.tsx
│   │   │       ├── avatar.tsx
│   │   │       ├── badge.tsx
│   │   │       ├── breadcrumb.tsx
│   │   │       ├── button.tsx
│   │   │       ├── calendar.tsx
│   │   │       ├── card.tsx
│   │   │       ├── carousel.tsx
│   │   │       ├── chart.tsx
│   │   │       ├── checkbox.tsx
│   │   │       ├── collapsible.tsx
│   │   │       ├── command.tsx
│   │   │       ├── context-menu.tsx
│   │   │       ├── dialog.tsx
│   │   │       ├── drawer.tsx
│   │   │       ├── dropdown-menu.tsx
│   │   │       ├── form.tsx
│   │   │       ├── hover-card.tsx
│   │   │       ├── input-otp.tsx
│   │   │       ├── input.tsx
│   │   │       ├── label.tsx
│   │   │       ├── menubar.tsx
│   │   │       ├── navigation-menu.tsx
│   │   │       ├── pagination.tsx
│   │   │       ├── popover.tsx
│   │   │       ├── progress.tsx
│   │   │       ├── radio-group.tsx
│   │   │       ├── resizable.tsx
│   │   │       ├── scroll-area.tsx
│   │   │       ├── select.tsx
│   │   │       ├── separator.tsx
│   │   │       ├── sheet.tsx
│   │   │       ├── sidebar.tsx
│   │   │       ├── skeleton.tsx
│   │   │       ├── slider.tsx
│   │   │       ├── sonner.tsx
│   │   │       ├── switch.tsx
│   │   │       ├── table.tsx
│   │   │       ├── tabs.tsx
│   │   │       ├── textarea.tsx
│   │   │       ├── toast.tsx
│   │   │       ├── toaster.tsx
│   │   │       ├── toggle-group.tsx
│   │   │       ├── toggle.tsx
│   │   │       ├── tooltip.tsx
│   │   │       └── use-toast.ts
│   │   ├── hooks/
│   │   │   ├── use-mobile.tsx
│   │   │   ├── use-toast.ts
│   │   │   └── useSummerSchoolSearch.ts
│   │   ├── index.css
│   │   ├── integrations/
│   │   │   └── supabase/
│   │   │       ├── client.ts
│   │   │       └── types.ts
│   │   ├── lib/
│   │   │   └── utils.ts
│   │   ├── main.tsx
│   │   ├── pages/
│   │   │   ├── Index.tsx
│   │   │   └── NotFound.tsx
│   │   ├── test/
│   │   │   ├── example.test.ts
│   │   │   └── setup.ts
│   │   ├── types/
│   │   │   └── summer-school.ts
│   │   └── vite-env.d.ts
│   ├── supabase/
│   │   ├── config.toml
│   │   └── functions/
│   │       ├── discover-schools/
│   │       │   └── index.ts
│   │       ├── mino-search/
│   │       │   └── index.ts
│   │       └── mino-search-stream/
│   │           └── index.ts
│   ├── tailwind.config.ts
│   ├── tsconfig.app.json
│   ├── tsconfig.json
│   ├── tsconfig.node.json
│   ├── vite.config.ts
│   └── vitest.config.ts
├── tenders-finder/
│   ├── .env.example
│   ├── .gitignore
│   ├── README.md
│   ├── bun.lockb
│   ├── components.json
│   ├── eslint.config.js
│   ├── index.html
│   ├── package.json
│   ├── postcss.config.js
│   ├── public/
│   │   └── robots.txt
│   ├── src/
│   │   ├── App.css
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── NavLink.tsx
│   │   │   ├── tender/
│   │   │   │   ├── AgentPreviewCard.tsx
│   │   │   │   ├── AgentPreviewGrid.tsx
│   │   │   │   ├── CompareButton.tsx
│   │   │   │   ├── CompareModal.tsx
│   │   │   │   ├── Header.tsx
│   │   │   │   ├── LinkConfigPage.tsx
│   │   │   │   ├── LiveBrowserModal.tsx
│   │   │   │   ├── SectorIcon.tsx
│   │   │   │   ├── SectorSelector.tsx
│   │   │   │   ├── TenderResultCard.tsx
│   │   │   │   └── TenderResultsList.tsx
│   │   │   └── ui/
│   │   │       ├── accordion.tsx
│   │   │       ├── alert-dialog.tsx
│   │   │       ├── alert.tsx
│   │   │       ├── aspect-ratio.tsx
│   │   │       ├── avatar.tsx
│   │   │       ├── badge.tsx
│   │   │       ├── breadcrumb.tsx
│   │   │       ├── button.tsx
│   │   │       ├── calendar.tsx
│   │   │       ├── card.tsx
│   │   │       ├── carousel.tsx
│   │   │       ├── chart.tsx
│   │   │       ├── checkbox.tsx
│   │   │       ├── collapsible.tsx
│   │   │       ├── command.tsx
│   │   │       ├── context-menu.tsx
│   │   │       ├── dialog.tsx
│   │   │       ├── drawer.tsx
│   │   │       ├── dropdown-menu.tsx
│   │   │       ├── form.tsx
│   │   │       ├── hover-card.tsx
│   │   │       ├── input-otp.tsx
│   │   │       ├── input.tsx
│   │   │       ├── label.tsx
│   │   │       ├── menubar.tsx
│   │   │       ├── navigation-menu.tsx
│   │   │       ├── pagination.tsx
│   │   │       ├── popover.tsx
│   │   │       ├── progress.tsx
│   │   │       ├── radio-group.tsx
│   │   │       ├── resizable.tsx
│   │   │       ├── scroll-area.tsx
│   │   │       ├── select.tsx
│   │   │       ├── separator.tsx
│   │   │       ├── sheet.tsx
│   │   │       ├── sidebar.tsx
│   │   │       ├── skeleton.tsx
│   │   │       ├── slider.tsx
│   │   │       ├── sonner.tsx
│   │   │       ├── switch.tsx
│   │   │       ├── table.tsx
│   │   │       ├── tabs.tsx
│   │   │       ├── textarea.tsx
│   │   │       ├── toast.tsx
│   │   │       ├── toaster.tsx
│   │   │       ├── toggle-group.tsx
│   │   │       ├── toggle.tsx
│   │   │       ├── tooltip.tsx
│   │   │       └── use-toast.ts
│   │   ├── hooks/
│   │   │   ├── use-mobile.tsx
│   │   │   ├── use-toast.ts
│   │   │   └── useTenderSearch.ts
│   │   ├── index.css
│   │   ├── integrations/
│   │   │   └── supabase/
│   │   │       ├── client.ts
│   │   │       └── types.ts
│   │   ├── lib/
│   │   │   ├── api/
│   │   │   │   └── tinyfish.ts
│   │   │   └── utils.ts
│   │   ├── main.tsx
│   │   ├── pages/
│   │   │   ├── Index.tsx
│   │   │   └── NotFound.tsx
│   │   ├── test/
│   │   │   └── setup.ts
│   │   ├── types/
│   │   │   └── tender.ts
│   │   └── vite-env.d.ts
│   ├── supabase/
│   │   ├── config.toml
│   │   └── functions/
│   │       ├── discover-tender-links/
│   │       │   └── index.ts
│   │       └── tinyfish-tender-search/
│   │           └── index.ts
│   ├── tailwind.config.ts
│   ├── tsconfig.app.json
│   ├── tsconfig.json
│   ├── tsconfig.node.json
│   ├── vite.config.ts
│   └── vitest.config.ts
├── tinyskills/
│   ├── .gitignore
│   ├── README.md
│   ├── app/
│   │   ├── api/
│   │   │   ├── identify-sources/
│   │   │   │   └── route.ts
│   │   │   ├── scrape-sources/
│   │   │   │   └── route.ts
│   │   │   └── synthesize/
│   │   │       └── route.ts
│   │   ├── globals.css
│   │   ├── history/
│   │   │   └── page.tsx
│   │   ├── layout.tsx
│   │   ├── page.tsx
│   │   └── settings/
│   │       └── page.tsx
│   ├── components/
│   │   ├── skillforge/
│   │   │   ├── nav-header.tsx
│   │   │   ├── phase-indicator.tsx
│   │   │   ├── skill-output.tsx
│   │   │   ├── skill-preview.tsx
│   │   │   ├── skill-raw.tsx
│   │   │   ├── skill-sources.tsx
│   │   │   ├── source-progress.tsx
│   │   │   ├── source-toggles.tsx
│   │   │   └── terminal-input.tsx
│   │   └── ui/
│   │       ├── badge.tsx
│   │       ├── button.tsx
│   │       ├── card.tsx
│   │       ├── input.tsx
│   │       ├── progress.tsx
│   │       ├── scroll-area.tsx
│   │       ├── sonner.tsx
│   │       ├── switch.tsx
│   │       ├── tabs.tsx
│   │       └── tooltip.tsx
│   ├── components.json
│   ├── eslint.config.mjs
│   ├── hooks/
│   │   ├── use-generation.ts
│   │   └── use-local-storage.ts
│   ├── lib/
│   │   ├── ai-client.ts
│   │   ├── mino-client.ts
│   │   ├── storage.ts
│   │   └── utils.ts
│   ├── next.config.ts
│   ├── package.json
│   ├── postcss.config.mjs
│   ├── public/
│   │   └── tinyfish.avif
│   ├── tsconfig.json
│   └── types/
│       └── index.ts
├── tutor-finder/
│   ├── .env.example
│   ├── .gitignore
│   ├── README.md
│   ├── bun.lockb
│   ├── components.json
│   ├── eslint.config.js
│   ├── index.html
│   ├── package.json
│   ├── postcss.config.js
│   ├── public/
│   │   └── robots.txt
│   ├── src/
│   │   ├── App.css
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── AgentPreviewCard.tsx
│   │   │   ├── AgentPreviewGrid.tsx
│   │   │   ├── CompareButton.tsx
│   │   │   ├── CompareDashboard.tsx
│   │   │   ├── DiscoveringState.tsx
│   │   │   ├── ExamSelector.tsx
│   │   │   ├── LocationInput.tsx
│   │   │   ├── NavLink.tsx
│   │   │   ├── TutorCard.tsx
│   │   │   ├── TutorResultsGrid.tsx
│   │   │   └── ui/
│   │   │       ├── accordion.tsx
│   │   │       ├── alert-dialog.tsx
│   │   │       ├── alert.tsx
│   │   │       ├── aspect-ratio.tsx
│   │   │       ├── avatar.tsx
│   │   │       ├── badge.tsx
│   │   │       ├── breadcrumb.tsx
│   │   │       ├── button.tsx
│   │   │       ├── calendar.tsx
│   │   │       ├── card.tsx
│   │   │       ├── carousel.tsx
│   │   │       ├── chart.tsx
│   │   │       ├── checkbox.tsx
│   │   │       ├── collapsible.tsx
│   │   │       ├── command.tsx
│   │   │       ├── context-menu.tsx
│   │   │       ├── dialog.tsx
│   │   │       ├── drawer.tsx
│   │   │       ├── dropdown-menu.tsx
│   │   │       ├── form.tsx
│   │   │       ├── hover-card.tsx
│   │   │       ├── input-otp.tsx
│   │   │       ├── input.tsx
│   │   │       ├── label.tsx
│   │   │       ├── menubar.tsx
│   │   │       ├── navigation-menu.tsx
│   │   │       ├── pagination.tsx
│   │   │       ├── popover.tsx
│   │   │       ├── progress.tsx
│   │   │       ├── radio-group.tsx
│   │   │       ├── resizable.tsx
│   │   │       ├── scroll-area.tsx
│   │   │       ├── select.tsx
│   │   │       ├── separator.tsx
│   │   │       ├── sheet.tsx
│   │   │       ├── sidebar.tsx
│   │   │       ├── skeleton.tsx
│   │   │       ├── slider.tsx
│   │   │       ├── sonner.tsx
│   │   │       ├── switch.tsx
│   │   │       ├── table.tsx
│   │   │       ├── tabs.tsx
│   │   │       ├── textarea.tsx
│   │   │       ├── toast.tsx
│   │   │       ├── toaster.tsx
│   │   │       ├── toggle-group.tsx
│   │   │       ├── toggle.tsx
│   │   │       ├── tooltip.tsx
│   │   │       └── use-toast.ts
│   │   ├── hooks/
│   │   │   ├── use-mobile.tsx
│   │   │   ├── use-toast.ts
│   │   │   └── useTutorSearch.ts
│   │   ├── index.css
│   │   ├── integrations/
│   │   │   └── supabase/
│   │   │       ├── client.ts
│   │   │       └── types.ts
│   │   ├── lib/
│   │   │   └── utils.ts
│   │   ├── main.tsx
│   │   ├── pages/
│   │   │   ├── Index.tsx
│   │   │   └── NotFound.tsx
│   │   ├── test/
│   │   │   ├── example.test.ts
│   │   │   └── setup.ts
│   │   ├── types/
│   │   │   └── tutor.ts
│   │   └── vite-env.d.ts
│   ├── supabase/
│   │   ├── config.toml
│   │   └── functions/
│   │       ├── discover-tutor-websites/
│   │       │   └── index.ts
│   │       └── search-tutors-mino/
│   │           └── index.ts
│   ├── tailwind.config.ts
│   ├── tsconfig.app.json
│   ├── tsconfig.json
│   ├── tsconfig.node.json
│   ├── vite.config.ts
│   └── vitest.config.ts
├── viet-bike-scout/
│   ├── .env.example
│   ├── .gitignore
│   ├── README.md
│   ├── components.json
│   ├── docs/
│   │   ├── PRD.md
│   │   └── use-case-brief.md
│   ├── eslint.config.mjs
│   ├── next.config.ts
│   ├── package.json
│   ├── postcss.config.mjs
│   ├── src/
│   │   ├── app/
│   │   │   ├── api/
│   │   │   │   └── search/
│   │   │   │       └── route.ts
│   │   │   ├── globals.css
│   │   │   ├── layout.tsx
│   │   │   └── page.tsx
│   │   ├── components/
│   │   │   ├── bike-card.tsx
│   │   │   ├── live-preview-grid.tsx
│   │   │   ├── results-grid.tsx
│   │   │   ├── shop-group.tsx
│   │   │   └── ui/
│   │   │       ├── badge.tsx
│   │   │       ├── button.tsx
│   │   │       ├── card.tsx
│   │   │       ├── skeleton.tsx
│   │   │       └── switch.tsx
│   │   ├── hooks/
│   │   │   └── use-bike-search.ts
│   │   └── lib/
│   │       ├── env.ts
│   │       ├── supabase.ts
│   │       └── utils.ts
│   ├── supabase/
│   │   ├── .gitignore
│   │   ├── config.toml
│   │   └── migrations/
│   │       └── 20260224170438_create_bike_cache.sql
│   └── tsconfig.json
├── waifu-deal-sniper/
│   ├── .gitignore
│   ├── README.md
│   ├── bot.js
│   ├── database.js
│   ├── package.json
│   └── templates.js
└── wing-command/
    ├── .eslintrc.json
    ├── .gitignore
    ├── README.md
    ├── app/
    │   ├── api/
    │   │   ├── deals/
    │   │   │   └── route.ts
    │   │   ├── menu/
    │   │   │   └── route.ts
    │   │   └── scout/
    │   │       └── route.ts
    │   ├── error.tsx
    │   ├── global-error.tsx
    │   ├── globals.css
    │   ├── layout.tsx
    │   ├── loading.tsx
    │   └── page.tsx
    ├── components/
    │   ├── AnimatedFieldBackground.tsx
    │   ├── BannerBreak.tsx
    │   ├── CoachHero.tsx
    │   ├── CoachWingMascot.tsx
    │   ├── CoinToss.tsx
    │   ├── ComicHero.tsx
    │   ├── CommandJumbotron.tsx
    │   ├── CompareBar.tsx
    │   ├── CompareModal.tsx
    │   ├── DealsView.tsx
    │   ├── FlavorSelector.tsx
    │   ├── FlavorTarot.tsx
    │   ├── FrostedGlassPanel.tsx
    │   ├── GlassBlitzEntrance.tsx
    │   ├── HeroVisuals.tsx
    │   ├── JumbotronSearch.tsx
    │   ├── MenuModal.tsx
    │   ├── PlaybookSearch.tsx
    │   ├── ScoutingReportCard.tsx
    │   ├── SunnyFieldEntrance.tsx
    │   ├── TacticalCanvas.tsx
    │   ├── TradingCardGrid.tsx
    │   ├── TrashTalkTicker.tsx
    │   ├── WingGrid.tsx
    │   ├── ZipSearch.tsx
    │   └── ui/
    │       ├── Accordion.tsx
    │       ├── Badge.tsx
    │       ├── Button.tsx
    │       ├── Input.tsx
    │       ├── Sheet.tsx
    │       └── index.ts
    ├── lib/
    │   ├── cache.ts
    │   ├── chain-prices.ts
    │   ├── deals.ts
    │   ├── env.ts
    │   ├── geocode.ts
    │   ├── menu.ts
    │   ├── seed-data.ts
    │   ├── supabase.ts
    │   ├── tinyfish-scraper.ts
    │   ├── types.ts
    │   └── utils.ts
    ├── next.config.mjs
    ├── package.json
    ├── postcss.config.js
    ├── render.yaml
    ├── scraper/
    │   ├── requirements.txt
    │   └── scrape_wings.py
    ├── scripts/
    │   └── cache-warmer.ts
    ├── supabase/
    │   └── schema.sql
    ├── tailwind.config.ts
    └── tsconfig.json
Download .txt
Showing preview only (255K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (2815 symbols across 701 files)

FILE: Manga-Availability-Finder/src/components/AgentCard.tsx
  type AgentStatus (line 4) | type AgentStatus = "idle" | "searching" | "found" | "not_found" | "error";
  type AgentCardProps (line 6) | interface AgentCardProps {
  function AgentCard (line 43) | function AgentCard({

FILE: Manga-Availability-Finder/src/components/NavLink.tsx
  type NavLinkCompatProps (line 5) | interface NavLinkCompatProps extends Omit<NavLinkProps, "className"> {

FILE: Manga-Availability-Finder/src/components/ResultsSummary.tsx
  type SearchResult (line 4) | interface SearchResult {
  type ResultsSummaryProps (line 10) | interface ResultsSummaryProps {
  function ResultsSummary (line 16) | function ResultsSummary({ mangaTitle, results, isSearching }: ResultsSum...

FILE: Manga-Availability-Finder/src/components/SearchHero.tsx
  type SearchHeroProps (line 6) | interface SearchHeroProps {
  function SearchHero (line 11) | function SearchHero({ onSearch, isSearching }: SearchHeroProps) {

FILE: Manga-Availability-Finder/src/components/ui/button.tsx
  type ButtonProps (line 37) | interface ButtonProps

FILE: Manga-Availability-Finder/src/hooks/useMangaSearch.ts
  type SiteAgent (line 5) | interface SiteAgent {
  function useMangaSearch (line 14) | function useMangaSearch() {

FILE: Manga-Availability-Finder/src/integrations/supabase/client.ts
  constant SUPABASE_URL (line 5) | const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL;
  constant SUPABASE_PUBLISHABLE_KEY (line 6) | const SUPABASE_PUBLISHABLE_KEY = import.meta.env.VITE_SUPABASE_PUBLISHAB...

FILE: Manga-Availability-Finder/src/integrations/supabase/types.ts
  type Json (line 1) | type Json =
  type Database (line 9) | type Database = {
  type DatabaseWithoutInternals (line 34) | type DatabaseWithoutInternals = Omit<Database, "__InternalSupabase">
  type DefaultSchema (line 36) | type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, "p...
  type Tables (line 38) | type Tables<
  type TablesInsert (line 67) | type TablesInsert<
  type TablesUpdate (line 92) | type TablesUpdate<
  type Enums (line 117) | type Enums<
  type CompositeTypes (line 134) | type CompositeTypes<

FILE: Manga-Availability-Finder/src/lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {

FILE: Manga-Availability-Finder/supabase/functions/search-manga/index.ts
  method start (line 83) | async start(controller) {

FILE: anime-watch-hub/app/api/check-platform/route.ts
  function POST (line 3) | async function POST(request: NextRequest) {

FILE: anime-watch-hub/app/api/discover-platforms/route.ts
  constant GEMINI_MODELS (line 3) | const GEMINI_MODELS = [
  function callGeminiWithRetry (line 9) | async function callGeminiWithRetry(
  function POST (line 79) | async function POST(request: NextRequest) {

FILE: anime-watch-hub/app/layout.tsx
  function RootLayout (line 33) | function RootLayout({

FILE: anime-watch-hub/app/loading.tsx
  function Loading (line 1) | function Loading() {

FILE: anime-watch-hub/app/page.tsx
  function Loading (line 4) | function Loading() {
  function Page (line 12) | function Page() {

FILE: anime-watch-hub/components/anime-watch-hub.tsx
  function AnimeWatchHub (line 14) | function AnimeWatchHub() {

FILE: anime-watch-hub/components/platform-card.tsx
  type PlatformCardProps (line 9) | interface PlatformCardProps {
  function PlatformCard (line 13) | function PlatformCard({ agent }: PlatformCardProps) {

FILE: anime-watch-hub/components/results-sidebar.tsx
  type ResultsSidebarProps (line 9) | interface ResultsSidebarProps {
  function ResultsSidebar (line 14) | function ResultsSidebar({ agents, animeTitle }: ResultsSidebarProps) {

FILE: anime-watch-hub/components/theme-provider.tsx
  function ThemeProvider (line 9) | function ThemeProvider({ children, ...props }: ThemeProviderProps) {

FILE: anime-watch-hub/components/ui/accordion.tsx
  function Accordion (line 9) | function Accordion({
  function AccordionItem (line 15) | function AccordionItem({
  function AccordionTrigger (line 28) | function AccordionTrigger({
  function AccordionContent (line 50) | function AccordionContent({

FILE: anime-watch-hub/components/ui/alert-dialog.tsx
  function AlertDialog (line 9) | function AlertDialog({
  function AlertDialogTrigger (line 15) | function AlertDialogTrigger({
  function AlertDialogPortal (line 23) | function AlertDialogPortal({
  function AlertDialogOverlay (line 31) | function AlertDialogOverlay({
  function AlertDialogContent (line 47) | function AlertDialogContent({
  function AlertDialogHeader (line 66) | function AlertDialogHeader({
  function AlertDialogFooter (line 79) | function AlertDialogFooter({
  function AlertDialogTitle (line 95) | function AlertDialogTitle({
  function AlertDialogDescription (line 108) | function AlertDialogDescription({
  function AlertDialogAction (line 121) | function AlertDialogAction({
  function AlertDialogCancel (line 133) | function AlertDialogCancel({

FILE: anime-watch-hub/components/ui/alert.tsx
  function Alert (line 22) | function Alert({
  function AlertTitle (line 37) | function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) {
  function AlertDescription (line 50) | function AlertDescription({

FILE: anime-watch-hub/components/ui/aspect-ratio.tsx
  function AspectRatio (line 5) | function AspectRatio({

FILE: anime-watch-hub/components/ui/avatar.tsx
  function Avatar (line 8) | function Avatar({
  function AvatarImage (line 24) | function AvatarImage({
  function AvatarFallback (line 37) | function AvatarFallback({

FILE: anime-watch-hub/components/ui/badge.tsx
  function Badge (line 28) | function Badge({

FILE: anime-watch-hub/components/ui/breadcrumb.tsx
  function Breadcrumb (line 7) | function Breadcrumb({ ...props }: React.ComponentProps<'nav'>) {
  function BreadcrumbList (line 11) | function BreadcrumbList({ className, ...props }: React.ComponentProps<'o...
  function BreadcrumbItem (line 24) | function BreadcrumbItem({ className, ...props }: React.ComponentProps<'l...
  function BreadcrumbLink (line 34) | function BreadcrumbLink({
  function BreadcrumbPage (line 52) | function BreadcrumbPage({ className, ...props }: React.ComponentProps<'s...
  function BreadcrumbSeparator (line 65) | function BreadcrumbSeparator({
  function BreadcrumbEllipsis (line 83) | function BreadcrumbEllipsis({

FILE: anime-watch-hub/components/ui/button-group.tsx
  function ButtonGroup (line 24) | function ButtonGroup({
  function ButtonGroupText (line 40) | function ButtonGroupText({
  function ButtonGroupSeparator (line 60) | function ButtonGroupSeparator({

FILE: anime-watch-hub/components/ui/button.tsx
  function Button (line 39) | function Button({

FILE: anime-watch-hub/components/ui/calendar.tsx
  function Calendar (line 14) | function Calendar({
  function CalendarDayButton (line 175) | function CalendarDayButton({

FILE: anime-watch-hub/components/ui/card.tsx
  function Card (line 5) | function Card({ className, ...props }: React.ComponentProps<'div'>) {
  function CardHeader (line 18) | function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
  function CardTitle (line 31) | function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
  function CardDescription (line 41) | function CardDescription({ className, ...props }: React.ComponentProps<'...
  function CardAction (line 51) | function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
  function CardContent (line 64) | function CardContent({ className, ...props }: React.ComponentProps<'div'...
  function CardFooter (line 74) | function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {

FILE: anime-watch-hub/components/ui/carousel.tsx
  type CarouselApi (line 12) | type CarouselApi = UseEmblaCarouselType[1]
  type UseCarouselParameters (line 13) | type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
  type CarouselOptions (line 14) | type CarouselOptions = UseCarouselParameters[0]
  type CarouselPlugin (line 15) | type CarouselPlugin = UseCarouselParameters[1]
  type CarouselProps (line 17) | type CarouselProps = {
  type CarouselContextProps (line 24) | type CarouselContextProps = {
  function useCarousel (line 35) | function useCarousel() {
  function Carousel (line 45) | function Carousel({
  function CarouselContent (line 135) | function CarouselContent({ className, ...props }: React.ComponentProps<'...
  function CarouselItem (line 156) | function CarouselItem({ className, ...props }: React.ComponentProps<'div...
  function CarouselPrevious (line 174) | function CarouselPrevious({
  function CarouselNext (line 204) | function CarouselNext({

FILE: anime-watch-hub/components/ui/chart.tsx
  constant THEMES (line 9) | const THEMES = { light: '', dark: '.dark' } as const
  type ChartConfig (line 11) | type ChartConfig = {
  type ChartContextProps (line 21) | type ChartContextProps = {
  function useChart (line 27) | function useChart() {
  function ChartContainer (line 37) | function ChartContainer({
  function ChartTooltipContent (line 107) | function ChartTooltipContent({
  function ChartLegendContent (line 253) | function ChartLegendContent({
  function getPayloadConfigFromPayload (line 308) | function getPayloadConfigFromPayload(

FILE: anime-watch-hub/components/ui/checkbox.tsx
  function Checkbox (line 9) | function Checkbox({

FILE: anime-watch-hub/components/ui/collapsible.tsx
  function Collapsible (line 5) | function Collapsible({
  function CollapsibleTrigger (line 11) | function CollapsibleTrigger({
  function CollapsibleContent (line 22) | function CollapsibleContent({

FILE: anime-watch-hub/components/ui/command.tsx
  function Command (line 16) | function Command({
  function CommandDialog (line 32) | function CommandDialog({
  function CommandInput (line 63) | function CommandInput({
  function CommandList (line 85) | function CommandList({
  function CommandEmpty (line 101) | function CommandEmpty({
  function CommandGroup (line 113) | function CommandGroup({
  function CommandSeparator (line 129) | function CommandSeparator({
  function CommandItem (line 142) | function CommandItem({
  function CommandShortcut (line 158) | function CommandShortcut({

FILE: anime-watch-hub/components/ui/context-menu.tsx
  function ContextMenu (line 9) | function ContextMenu({
  function ContextMenuTrigger (line 15) | function ContextMenuTrigger({
  function ContextMenuGroup (line 23) | function ContextMenuGroup({
  function ContextMenuPortal (line 31) | function ContextMenuPortal({
  function ContextMenuSub (line 39) | function ContextMenuSub({
  function ContextMenuRadioGroup (line 45) | function ContextMenuRadioGroup({
  function ContextMenuSubTrigger (line 56) | function ContextMenuSubTrigger({
  function ContextMenuSubContent (line 80) | function ContextMenuSubContent({
  function ContextMenuContent (line 96) | function ContextMenuContent({
  function ContextMenuItem (line 114) | function ContextMenuItem({
  function ContextMenuCheckboxItem (line 137) | function ContextMenuCheckboxItem({
  function ContextMenuRadioItem (line 163) | function ContextMenuRadioItem({
  function ContextMenuLabel (line 187) | function ContextMenuLabel({
  function ContextMenuSeparator (line 207) | function ContextMenuSeparator({
  function ContextMenuShortcut (line 220) | function ContextMenuShortcut({

FILE: anime-watch-hub/components/ui/dialog.tsx
  function Dialog (line 9) | function Dialog({
  function DialogTrigger (line 15) | function DialogTrigger({
  function DialogPortal (line 21) | function DialogPortal({
  function DialogClose (line 27) | function DialogClose({
  function DialogOverlay (line 33) | function DialogOverlay({
  function DialogContent (line 49) | function DialogContent({
  function DialogHeader (line 83) | function DialogHeader({ className, ...props }: React.ComponentProps<'div...
  function DialogFooter (line 93) | function DialogFooter({ className, ...props }: React.ComponentProps<'div...
  function DialogTitle (line 106) | function DialogTitle({
  function DialogDescription (line 119) | function DialogDescription({

FILE: anime-watch-hub/components/ui/drawer.tsx
  function Drawer (line 8) | function Drawer({
  function DrawerTrigger (line 14) | function DrawerTrigger({
  function DrawerPortal (line 20) | function DrawerPortal({
  function DrawerClose (line 26) | function DrawerClose({
  function DrawerOverlay (line 32) | function DrawerOverlay({
  function DrawerContent (line 48) | function DrawerContent({
  function DrawerHeader (line 75) | function DrawerHeader({ className, ...props }: React.ComponentProps<'div...
  function DrawerFooter (line 88) | function DrawerFooter({ className, ...props }: React.ComponentProps<'div...
  function DrawerTitle (line 98) | function DrawerTitle({
  function DrawerDescription (line 111) | function DrawerDescription({

FILE: anime-watch-hub/components/ui/dropdown-menu.tsx
  function DropdownMenu (line 9) | function DropdownMenu({
  function DropdownMenuPortal (line 15) | function DropdownMenuPortal({
  function DropdownMenuTrigger (line 23) | function DropdownMenuTrigger({
  function DropdownMenuContent (line 34) | function DropdownMenuContent({
  function DropdownMenuGroup (line 54) | function DropdownMenuGroup({
  function DropdownMenuItem (line 62) | function DropdownMenuItem({
  function DropdownMenuCheckboxItem (line 85) | function DropdownMenuCheckboxItem({
  function DropdownMenuRadioGroup (line 111) | function DropdownMenuRadioGroup({
  function DropdownMenuRadioItem (line 122) | function DropdownMenuRadioItem({
  function DropdownMenuLabel (line 146) | function DropdownMenuLabel({
  function DropdownMenuSeparator (line 166) | function DropdownMenuSeparator({
  function DropdownMenuShortcut (line 179) | function DropdownMenuShortcut({
  function DropdownMenuSub (line 195) | function DropdownMenuSub({
  function DropdownMenuSubTrigger (line 201) | function DropdownMenuSubTrigger({
  function DropdownMenuSubContent (line 225) | function DropdownMenuSubContent({

FILE: anime-watch-hub/components/ui/empty.tsx
  function Empty (line 5) | function Empty({ className, ...props }: React.ComponentProps<'div'>) {
  function EmptyHeader (line 18) | function EmptyHeader({ className, ...props }: React.ComponentProps<'div'...
  function EmptyMedia (line 46) | function EmptyMedia({
  function EmptyTitle (line 61) | function EmptyTitle({ className, ...props }: React.ComponentProps<'div'>) {
  function EmptyDescription (line 71) | function EmptyDescription({ className, ...props }: React.ComponentProps<...
  function EmptyContent (line 84) | function EmptyContent({ className, ...props }: React.ComponentProps<'div...

FILE: anime-watch-hub/components/ui/field.tsx
  function FieldSet (line 10) | function FieldSet({ className, ...props }: React.ComponentProps<'fieldse...
  function FieldLegend (line 24) | function FieldLegend({
  function FieldGroup (line 44) | function FieldGroup({ className, ...props }: React.ComponentProps<'div'>) {
  function Field (line 81) | function Field({
  function FieldContent (line 97) | function FieldContent({ className, ...props }: React.ComponentProps<'div...
  function FieldLabel (line 110) | function FieldLabel({
  function FieldTitle (line 128) | function FieldTitle({ className, ...props }: React.ComponentProps<'div'>) {
  function FieldDescription (line 141) | function FieldDescription({ className, ...props }: React.ComponentProps<...
  function FieldSeparator (line 156) | function FieldSeparator({
  function FieldError (line 186) | function FieldError({

FILE: anime-watch-hub/components/ui/form.tsx
  type FormFieldContextValue (line 21) | type FormFieldContextValue<
  type FormItemContextValue (line 68) | type FormItemContextValue = {
  function FormItem (line 76) | function FormItem({ className, ...props }: React.ComponentProps<'div'>) {
  function FormLabel (line 90) | function FormLabel({
  function FormControl (line 107) | function FormControl({ ...props }: React.ComponentProps<typeof Slot>) {
  function FormDescription (line 125) | function FormDescription({ className, ...props }: React.ComponentProps<'...
  function FormMessage (line 138) | function FormMessage({ className, ...props }: React.ComponentProps<'p'>) {

FILE: anime-watch-hub/components/ui/hover-card.tsx
  function HoverCard (line 8) | function HoverCard({
  function HoverCardTrigger (line 14) | function HoverCardTrigger({
  function HoverCardContent (line 22) | function HoverCardContent({

FILE: anime-watch-hub/components/ui/input-group.tsx
  function InputGroup (line 10) | function InputGroup({ className, ...props }: React.ComponentProps<'div'>) {
  function InputGroupAddon (line 59) | function InputGroupAddon({
  function InputGroupButton (line 99) | function InputGroupButton({
  function InputGroupText (line 118) | function InputGroupText({ className, ...props }: React.ComponentProps<'s...
  function InputGroupInput (line 130) | function InputGroupInput({
  function InputGroupTextarea (line 146) | function InputGroupTextarea({

FILE: anime-watch-hub/components/ui/input-otp.tsx
  function InputOTP (line 9) | function InputOTP({
  function InputOTPGroup (line 29) | function InputOTPGroup({ className, ...props }: React.ComponentProps<'di...
  function InputOTPSlot (line 39) | function InputOTPSlot({
  function InputOTPSeparator (line 69) | function InputOTPSeparator({ ...props }: React.ComponentProps<'div'>) {

FILE: anime-watch-hub/components/ui/input.tsx
  function Input (line 5) | function Input({ className, type, ...props }: React.ComponentProps<'inpu...

FILE: anime-watch-hub/components/ui/item.tsx
  function ItemGroup (line 8) | function ItemGroup({ className, ...props }: React.ComponentProps<'div'>) {
  function ItemSeparator (line 19) | function ItemSeparator({
  function Item (line 54) | function Item({
  function ItemMedia (line 91) | function ItemMedia({
  function ItemContent (line 106) | function ItemContent({ className, ...props }: React.ComponentProps<'div'...
  function ItemTitle (line 119) | function ItemTitle({ className, ...props }: React.ComponentProps<'div'>) {
  function ItemDescription (line 132) | function ItemDescription({ className, ...props }: React.ComponentProps<'...
  function ItemActions (line 146) | function ItemActions({ className, ...props }: React.ComponentProps<'div'...
  function ItemHeader (line 156) | function ItemHeader({ className, ...props }: React.ComponentProps<'div'>) {
  function ItemFooter (line 169) | function ItemFooter({ className, ...props }: React.ComponentProps<'div'>) {

FILE: anime-watch-hub/components/ui/kbd.tsx
  function Kbd (line 3) | function Kbd({ className, ...props }: React.ComponentProps<'kbd'>) {
  function KbdGroup (line 18) | function KbdGroup({ className, ...props }: React.ComponentProps<'div'>) {

FILE: anime-watch-hub/components/ui/label.tsx
  function Label (line 8) | function Label({

FILE: anime-watch-hub/components/ui/menubar.tsx
  function Menubar (line 9) | function Menubar({
  function MenubarMenu (line 25) | function MenubarMenu({
  function MenubarGroup (line 31) | function MenubarGroup({
  function MenubarPortal (line 37) | function MenubarPortal({
  function MenubarRadioGroup (line 43) | function MenubarRadioGroup({
  function MenubarTrigger (line 51) | function MenubarTrigger({
  function MenubarContent (line 67) | function MenubarContent({
  function MenubarItem (line 91) | function MenubarItem({
  function MenubarCheckboxItem (line 114) | function MenubarCheckboxItem({
  function MenubarRadioItem (line 140) | function MenubarRadioItem({
  function MenubarLabel (line 164) | function MenubarLabel({
  function MenubarSeparator (line 184) | function MenubarSeparator({
  function MenubarShortcut (line 197) | function MenubarShortcut({
  function MenubarSub (line 213) | function MenubarSub({
  function MenubarSubTrigger (line 219) | function MenubarSubTrigger({
  function MenubarSubContent (line 243) | function MenubarSubContent({

FILE: anime-watch-hub/components/ui/navigation-menu.tsx
  function NavigationMenu (line 8) | function NavigationMenu({
  function NavigationMenuList (line 32) | function NavigationMenuList({
  function NavigationMenuItem (line 48) | function NavigationMenuItem({
  function NavigationMenuTrigger (line 65) | function NavigationMenuTrigger({
  function NavigationMenuContent (line 85) | function NavigationMenuContent({
  function NavigationMenuViewport (line 102) | function NavigationMenuViewport({
  function NavigationMenuLink (line 122) | function NavigationMenuLink({
  function NavigationMenuIndicator (line 138) | function NavigationMenuIndicator({

FILE: anime-watch-hub/components/ui/pagination.tsx
  function Pagination (line 11) | function Pagination({ className, ...props }: React.ComponentProps<'nav'>) {
  function PaginationContent (line 23) | function PaginationContent({
  function PaginationItem (line 36) | function PaginationItem({ ...props }: React.ComponentProps<'li'>) {
  type PaginationLinkProps (line 40) | type PaginationLinkProps = {
  function PaginationLink (line 45) | function PaginationLink({
  function PaginationPrevious (line 68) | function PaginationPrevious({
  function PaginationNext (line 85) | function PaginationNext({
  function PaginationEllipsis (line 102) | function PaginationEllipsis({

FILE: anime-watch-hub/components/ui/popover.tsx
  function Popover (line 8) | function Popover({
  function PopoverTrigger (line 14) | function PopoverTrigger({
  function PopoverContent (line 20) | function PopoverContent({
  function PopoverAnchor (line 42) | function PopoverAnchor({

FILE: anime-watch-hub/components/ui/progress.tsx
  function Progress (line 8) | function Progress({

FILE: anime-watch-hub/components/ui/radio-group.tsx
  function RadioGroup (line 9) | function RadioGroup({
  function RadioGroupItem (line 22) | function RadioGroupItem({

FILE: anime-watch-hub/components/ui/resizable.tsx
  function ResizablePanelGroup (line 9) | function ResizablePanelGroup({
  function ResizablePanel (line 25) | function ResizablePanel({
  function ResizableHandle (line 31) | function ResizableHandle({

FILE: anime-watch-hub/components/ui/scroll-area.tsx
  function ScrollArea (line 8) | function ScrollArea({
  function ScrollBar (line 31) | function ScrollBar({

FILE: anime-watch-hub/components/ui/select.tsx
  function Select (line 9) | function Select({
  function SelectGroup (line 15) | function SelectGroup({
  function SelectValue (line 21) | function SelectValue({
  function SelectTrigger (line 27) | function SelectTrigger({
  function SelectContent (line 53) | function SelectContent({
  function SelectLabel (line 88) | function SelectLabel({
  function SelectItem (line 101) | function SelectItem({
  function SelectSeparator (line 125) | function SelectSeparator({
  function SelectScrollUpButton (line 138) | function SelectScrollUpButton({
  function SelectScrollDownButton (line 156) | function SelectScrollDownButton({

FILE: anime-watch-hub/components/ui/separator.tsx
  function Separator (line 8) | function Separator({

FILE: anime-watch-hub/components/ui/sheet.tsx
  function Sheet (line 9) | function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive....
  function SheetTrigger (line 13) | function SheetTrigger({
  function SheetClose (line 19) | function SheetClose({
  function SheetPortal (line 25) | function SheetPortal({
  function SheetOverlay (line 31) | function SheetOverlay({
  function SheetContent (line 47) | function SheetContent({
  function SheetHeader (line 84) | function SheetHeader({ className, ...props }: React.ComponentProps<'div'...
  function SheetFooter (line 94) | function SheetFooter({ className, ...props }: React.ComponentProps<'div'...
  function SheetTitle (line 104) | function SheetTitle({
  function SheetDescription (line 117) | function SheetDescription({

FILE: anime-watch-hub/components/ui/sidebar.tsx
  constant SIDEBAR_COOKIE_NAME (line 28) | const SIDEBAR_COOKIE_NAME = 'sidebar_state'
  constant SIDEBAR_COOKIE_MAX_AGE (line 29) | const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7
  constant SIDEBAR_WIDTH (line 30) | const SIDEBAR_WIDTH = '16rem'
  constant SIDEBAR_WIDTH_MOBILE (line 31) | const SIDEBAR_WIDTH_MOBILE = '18rem'
  constant SIDEBAR_WIDTH_ICON (line 32) | const SIDEBAR_WIDTH_ICON = '3rem'
  constant SIDEBAR_KEYBOARD_SHORTCUT (line 33) | const SIDEBAR_KEYBOARD_SHORTCUT = 'b'
  type SidebarContextProps (line 35) | type SidebarContextProps = {
  function useSidebar (line 47) | function useSidebar() {
  function SidebarProvider (line 56) | function SidebarProvider({
  function Sidebar (line 154) | function Sidebar({
  function SidebarTrigger (line 256) | function SidebarTrigger({
  function SidebarRail (line 282) | function SidebarRail({ className, ...props }: React.ComponentProps<'butt...
  function SidebarInset (line 307) | function SidebarInset({ className, ...props }: React.ComponentProps<'mai...
  function SidebarInput (line 321) | function SidebarInput({
  function SidebarHeader (line 335) | function SidebarHeader({ className, ...props }: React.ComponentProps<'di...
  function SidebarFooter (line 346) | function SidebarFooter({ className, ...props }: React.ComponentProps<'di...
  function SidebarSeparator (line 357) | function SidebarSeparator({
  function SidebarContent (line 371) | function SidebarContent({ className, ...props }: React.ComponentProps<'d...
  function SidebarGroup (line 385) | function SidebarGroup({ className, ...props }: React.ComponentProps<'div...
  function SidebarGroupLabel (line 396) | function SidebarGroupLabel({
  function SidebarGroupAction (line 417) | function SidebarGroupAction({
  function SidebarGroupContent (line 440) | function SidebarGroupContent({
  function SidebarMenu (line 454) | function SidebarMenu({ className, ...props }: React.ComponentProps<'ul'>) {
  function SidebarMenuItem (line 465) | function SidebarMenuItem({ className, ...props }: React.ComponentProps<'...
  function SidebarMenuButton (line 498) | function SidebarMenuButton({
  function SidebarMenuAction (line 548) | function SidebarMenuAction({
  function SidebarMenuBadge (line 580) | function SidebarMenuBadge({
  function SidebarMenuSkeleton (line 602) | function SidebarMenuSkeleton({
  function SidebarMenuSub (line 640) | function SidebarMenuSub({ className, ...props }: React.ComponentProps<'u...
  function SidebarMenuSubItem (line 655) | function SidebarMenuSubItem({
  function SidebarMenuSubButton (line 669) | function SidebarMenuSubButton({

FILE: anime-watch-hub/components/ui/skeleton.tsx
  function Skeleton (line 3) | function Skeleton({ className, ...props }: React.ComponentProps<'div'>) {

FILE: anime-watch-hub/components/ui/slider.tsx
  function Slider (line 8) | function Slider({

FILE: anime-watch-hub/components/ui/spinner.tsx
  function Spinner (line 5) | function Spinner({ className, ...props }: React.ComponentProps<'svg'>) {

FILE: anime-watch-hub/components/ui/switch.tsx
  function Switch (line 8) | function Switch({

FILE: anime-watch-hub/components/ui/table.tsx
  function Table (line 7) | function Table({ className, ...props }: React.ComponentProps<'table'>) {
  function TableHeader (line 22) | function TableHeader({ className, ...props }: React.ComponentProps<'thea...
  function TableBody (line 32) | function TableBody({ className, ...props }: React.ComponentProps<'tbody'...
  function TableFooter (line 42) | function TableFooter({ className, ...props }: React.ComponentProps<'tfoo...
  function TableRow (line 55) | function TableRow({ className, ...props }: React.ComponentProps<'tr'>) {
  function TableHead (line 68) | function TableHead({ className, ...props }: React.ComponentProps<'th'>) {
  function TableCell (line 81) | function TableCell({ className, ...props }: React.ComponentProps<'td'>) {
  function TableCaption (line 94) | function TableCaption({

FILE: anime-watch-hub/components/ui/tabs.tsx
  function Tabs (line 8) | function Tabs({
  function TabsList (line 21) | function TabsList({
  function TabsTrigger (line 37) | function TabsTrigger({
  function TabsContent (line 53) | function TabsContent({

FILE: anime-watch-hub/components/ui/textarea.tsx
  function Textarea (line 5) | function Textarea({ className, ...props }: React.ComponentProps<'textare...

FILE: anime-watch-hub/components/ui/toast.tsx
  type ToastProps (line 115) | type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
  type ToastActionElement (line 117) | type ToastActionElement = React.ReactElement<typeof ToastAction>

FILE: anime-watch-hub/components/ui/toaster.tsx
  function Toaster (line 13) | function Toaster() {

FILE: anime-watch-hub/components/ui/toggle-group.tsx
  function ToggleGroup (line 17) | function ToggleGroup({
  function ToggleGroupItem (line 43) | function ToggleGroupItem({

FILE: anime-watch-hub/components/ui/toggle.tsx
  function Toggle (line 31) | function Toggle({

FILE: anime-watch-hub/components/ui/tooltip.tsx
  function TooltipProvider (line 8) | function TooltipProvider({
  function Tooltip (line 21) | function Tooltip({
  function TooltipTrigger (line 31) | function TooltipTrigger({
  function TooltipContent (line 37) | function TooltipContent({

FILE: anime-watch-hub/components/ui/use-mobile.tsx
  constant MOBILE_BREAKPOINT (line 3) | const MOBILE_BREAKPOINT = 768
  function useIsMobile (line 5) | function useIsMobile() {

FILE: anime-watch-hub/components/ui/use-toast.ts
  constant TOAST_LIMIT (line 8) | const TOAST_LIMIT = 1
  constant TOAST_REMOVE_DELAY (line 9) | const TOAST_REMOVE_DELAY = 1000000
  type ToasterToast (line 11) | type ToasterToast = ToastProps & {
  function genId (line 27) | function genId() {
  type ActionType (line 32) | type ActionType = typeof actionTypes
  type Action (line 34) | type Action =
  type State (line 52) | interface State {
  function dispatch (line 133) | function dispatch(action: Action) {
  type Toast (line 140) | type Toast = Omit<ToasterToast, 'id'>
  function toast (line 142) | function toast({ ...props }: Toast) {
  function useToast (line 171) | function useToast() {

FILE: anime-watch-hub/hooks/use-anime-search.ts
  type DiscoveredPlatform (line 6) | interface DiscoveredPlatform {
  function useAnimeSearch (line 12) | function useAnimeSearch() {

FILE: anime-watch-hub/hooks/use-mobile.ts
  constant MOBILE_BREAKPOINT (line 3) | const MOBILE_BREAKPOINT = 768
  function useIsMobile (line 5) | function useIsMobile() {

FILE: anime-watch-hub/hooks/use-toast.ts
  constant TOAST_LIMIT (line 8) | const TOAST_LIMIT = 1
  constant TOAST_REMOVE_DELAY (line 9) | const TOAST_REMOVE_DELAY = 1000000
  type ToasterToast (line 11) | type ToasterToast = ToastProps & {
  function genId (line 27) | function genId() {
  type ActionType (line 32) | type ActionType = typeof actionTypes
  type Action (line 34) | type Action =
  type State (line 52) | interface State {
  function dispatch (line 133) | function dispatch(action: Action) {
  type Toast (line 140) | type Toast = Omit<ToasterToast, 'id'>
  function toast (line 142) | function toast({ ...props }: Toast) {
  function useToast (line 171) | function useToast() {

FILE: anime-watch-hub/lib/types.ts
  type StreamingPlatform (line 1) | interface StreamingPlatform {
  type PlatformSearchResult (line 8) | interface PlatformSearchResult {
  type MinoAgentState (line 20) | interface MinoAgentState {
  constant STREAMING_PLATFORMS (line 36) | const STREAMING_PLATFORMS: StreamingPlatform[] = [

FILE: anime-watch-hub/lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {

FILE: bestbet/app/components/MoneyParticle.tsx
  type MoneyParticleProps (line 7) | type MoneyParticleProps = {
  function MoneyParticle (line 15) | function MoneyParticle({

FILE: bestbet/app/components/SportsbookSelector.tsx
  type Sportsbook (line 6) | type Sportsbook = {
  type SportsbookSelectorProps (line 13) | type SportsbookSelectorProps = {
  function SportsbookSelector (line 22) | function SportsbookSelector({

FILE: bestbet/app/layout.tsx
  function RootLayout (line 23) | function RootLayout({

FILE: bestbet/app/page.tsx
  constant MONEY_IMAGES (line 14) | const MONEY_IMAGES = ["/BBCoin.png", "/BBNote1.png", "/BBNote2.png"];
  type MoneyParticleType (line 16) | type MoneyParticleType = {
  constant DEFAULT_SPORTSBOOKS (line 23) | const DEFAULT_SPORTSBOOKS: Sportsbook[] = [
  constant STORAGE_KEY (line 32) | const STORAGE_KEY = "bestbet-sportsbooks";
  constant SELECTION_KEY (line 33) | const SELECTION_KEY = "bestbet-selected";
  function getCurrentDate (line 35) | function getCurrentDate(): string {
  type OddsResult (line 46) | type OddsResult = {
  type ErrorResult (line 59) | type ErrorResult = {
  type SportsbookResult (line 64) | type SportsbookResult = {
  function Home (line 69) | function Home() {

FILE: bestbet/app/webagent.ts
  constant ENDPOINT (line 1) | const ENDPOINT = "https://agent.tinyfish.ai/v1/automation/run-sse";
  type MinoSSECallbacks (line 3) | type MinoSSECallbacks = {
  function runMinoSSE (line 8) | async function runMinoSSE(

FILE: code-reference-finder/extension/sidepanel.js
  function injectCode (line 4) | function injectCode(code) {

FILE: code-reference-finder/next.config.ts
  method headers (line 4) | async headers() {

FILE: code-reference-finder/src/app/api/analyze/route.ts
  function POST (line 4) | async function POST(request: NextRequest) {

FILE: code-reference-finder/src/app/layout.tsx
  function RootLayout (line 22) | function RootLayout({

FILE: code-reference-finder/src/app/page.tsx
  function Home (line 9) | function Home() {

FILE: code-reference-finder/src/components/AgentCard.tsx
  type AgentCardProps (line 9) | interface AgentCardProps {
  function AgentCard (line 14) | function AgentCard({ agent, onPreviewClick }: AgentCardProps) {

FILE: code-reference-finder/src/components/AnalysisSummary.tsx
  type AnalysisSummaryProps (line 6) | interface AnalysisSummaryProps {
  function AnalysisSummary (line 10) | function AnalysisSummary({ analysis }: AnalysisSummaryProps) {

FILE: code-reference-finder/src/components/CodeInput.tsx
  type CodeInputProps (line 6) | interface CodeInputProps {
  constant EXAMPLES (line 11) | const EXAMPLES = [
  function CodeInput (line 48) | function CodeInput({ onSubmit, injectedCode }: CodeInputProps) {

FILE: code-reference-finder/src/components/Dashboard.tsx
  type DashboardProps (line 11) | interface DashboardProps {
  function Dashboard (line 16) | function Dashboard({ state, onCancel }: DashboardProps) {

FILE: code-reference-finder/src/components/Header.tsx
  type HeaderProps (line 6) | interface HeaderProps {
  function Header (line 11) | function Header({ showReset, onReset }: HeaderProps) {

FILE: code-reference-finder/src/components/LiveBrowserPreview.tsx
  type LiveBrowserPreviewProps (line 7) | interface LiveBrowserPreviewProps {
  function LiveBrowserPreview (line 13) | function LiveBrowserPreview({
  type MiniPreviewProps (line 94) | interface MiniPreviewProps {
  function MiniPreview (line 99) | function MiniPreview({ streamingUrl, onClick }: MiniPreviewProps) {

FILE: code-reference-finder/src/components/PipelineProgress.tsx
  constant STEPS (line 6) | const STEPS: { phase: AppPhase; label: string }[] = [
  constant PHASE_ORDER (line 12) | const PHASE_ORDER: AppPhase[] = ['input', 'analyzing', 'searching', 'ext...
  type PipelineProgressProps (line 14) | interface PipelineProgressProps {
  function PipelineProgress (line 18) | function PipelineProgress({ currentPhase }: PipelineProgressProps) {

FILE: code-reference-finder/src/components/ReferenceCard.tsx
  type ReferenceCardProps (line 17) | interface ReferenceCardProps {
  function ReferenceCard (line 21) | function ReferenceCard({ data }: ReferenceCardProps) {

FILE: code-reference-finder/src/components/ReferenceGrid.tsx
  type ReferenceGridProps (line 8) | interface ReferenceGridProps {
  function ReferenceGrid (line 13) | function ReferenceGrid({ agents, onPreviewClick }: ReferenceGridProps) {

FILE: code-reference-finder/src/context/AppContext.tsx
  function allAgentsDone (line 23) | function allAgentsDone(agents: Record<string, ReferenceAgentState>): boo...
  function appReducer (line 29) | function appReducer(state: AppState, action: AppAction): AppState {
  type AppContextValue (line 155) | interface AppContextValue {
  function AppProvider (line 162) | function AppProvider({ children }: { children: ReactNode }) {
  function useAppContext (line 171) | function useAppContext(): AppContextValue {

FILE: code-reference-finder/src/hooks/useCodeAnalysis.ts
  function useCodeAnalysis (line 14) | function useCodeAnalysis() {

FILE: code-reference-finder/src/lib/constants.ts
  constant MINO_API_URL (line 1) | const MINO_API_URL = 'https://agent.tinyfish.ai/v1/automation/run-sse';
  constant OPENROUTER_API_URL (line 2) | const OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/chat/completions';
  constant GITHUB_API_URL (line 3) | const GITHUB_API_URL = 'https://api.github.com';
  constant STACKEXCHANGE_API_URL (line 4) | const STACKEXCHANGE_API_URL = 'https://api.stackexchange.com/2.3';
  constant OPENROUTER_MODEL (line 6) | const OPENROUTER_MODEL = 'google/gemini-3.1-flash-lite-preview';
  constant OPENROUTER_TEMPERATURE (line 7) | const OPENROUTER_TEMPERATURE = 0.2;
  constant MAX_AGENTS (line 9) | const MAX_AGENTS = 10;
  constant AGENT_TIMEOUT_MS (line 10) | const AGENT_TIMEOUT_MS = 360_000;
  constant GITHUB_RESULTS_PER_QUERY (line 11) | const GITHUB_RESULTS_PER_QUERY = 8;
  constant STACKOVERFLOW_RESULTS_PER_QUERY (line 12) | const STACKOVERFLOW_RESULTS_PER_QUERY = 10;
  constant APP_NAME (line 14) | const APP_NAME = 'Code Reference Finder';
  constant APP_DESCRIPTION (line 15) | const APP_DESCRIPTION = 'Find real-world usage examples for unfamiliar c...

FILE: code-reference-finder/src/lib/goal-builder.ts
  function buildGitHubGoal (line 3) | function buildGitHubGoal(
  function buildSOReasoningGoal (line 47) | function buildSOReasoningGoal(

FILE: code-reference-finder/src/lib/mino-client.ts
  function parseSSELine (line 4) | function parseSSELine(line: string): MinoSSEEvent | null {
  function startMinoAgent (line 17) | function startMinoAgent(

FILE: code-reference-finder/src/lib/openrouter.ts
  function extractJSON (line 4) | function extractJSON(text: string): unknown {
  function callOpenRouter (line 17) | async function callOpenRouter(systemPrompt: string, userPrompt: string):...
  function analyzeCode (line 48) | async function analyzeCode(code: string): Promise<CodeAnalysis> {
  function generateSearchQueries (line 79) | async function generateSearchQueries(analysis: CodeAnalysis): Promise<Se...

FILE: code-reference-finder/src/lib/orchestrator.ts
  function emitEvent (line 10) | function emitEvent(
  function makeAgentId (line 20) | function makeAgentId(index: number, platform: string): string {
  function launchAgent (line 24) | function launchAgent(
  function runPipeline (line 117) | async function runPipeline(

FILE: code-reference-finder/src/lib/search.ts
  type GitHubRepoItem (line 10) | interface GitHubRepoItem {
  function delay (line 18) | function delay(ms: number): Promise<void> {
  function fetchWithRetry (line 22) | async function fetchWithRetry(
  function searchGitHub (line 46) | async function searchGitHub(query: string): Promise<SearchResult[]> {
  function searchStackOverflow (line 86) | async function searchStackOverflow(query: string): Promise<SearchResult[...
  function deduplicate (line 138) | function deduplicate(results: SearchResult[]): SearchResult[] {
  function executeSearches (line 147) | async function executeSearches(queries: SearchQuery[]): Promise<SearchRe...

FILE: code-reference-finder/src/lib/types.ts
  type SourcePlatform (line 2) | type SourcePlatform = 'github' | 'stackoverflow';
  type CodeAnalysis (line 5) | interface CodeAnalysis {
  type SearchQuery (line 13) | interface SearchQuery {
  type StackExchangeItem (line 20) | interface StackExchangeItem {
  type SearchResult (line 32) | interface SearchResult {
  type CodeSnippet (line 47) | interface CodeSnippet {
  type ReferenceData (line 54) | interface ReferenceData {
  type AgentStatus (line 74) | type AgentStatus =
  type AgentStep (line 82) | interface AgentStep {
  type ReferenceAgentState (line 87) | interface ReferenceAgentState {
  type AppPhase (line 102) | type AppPhase =
  type AppState (line 109) | interface AppState {
  type AppAction (line 121) | type AppAction =
  type MinoSSEEvent (line 133) | interface MinoSSEEvent {
  type MinoCallbacks (line 145) | interface MinoCallbacks {
  type MinoRequestConfig (line 152) | interface MinoRequestConfig {
  type OrchestratorEventType (line 158) | type OrchestratorEventType =
  type OrchestratorEvent (line 169) | interface OrchestratorEvent {

FILE: competitor-analysis/app/analysis/page.tsx
  function AnalysisPage (line 11) | function AnalysisPage() {

FILE: competitor-analysis/app/api/analyze-pricing/route.ts
  type PricingData (line 5) | interface PricingData {
  type Baseline (line 11) | interface Baseline {
  function POST (line 19) | async function POST(request: NextRequest) {
  function buildAnalysisPrompt (line 64) | function buildAnalysisPrompt(baseline: Baseline | null, pricingData: Pri...
  function calculateMarketPosition (line 98) | function calculateMarketPosition(baseline: Baseline | null, analysis: { ...

FILE: competitor-analysis/app/api/generate-urls/route.ts
  function POST (line 5) | async function POST(request: NextRequest) {

FILE: competitor-analysis/app/api/scrape-pricing/route.ts
  type Competitor (line 4) | interface Competitor {
  constant SCRAPING_GOALS (line 11) | const SCRAPING_GOALS: Record<DetailLevel, string> = {
  function POST (line 158) | async function POST(request: NextRequest) {
  function transformToNewSchema (line 268) | function transformToNewSchema(
  function scrapePricingPage (line 319) | async function scrapePricingPage(

FILE: competitor-analysis/app/company/[id]/page.tsx
  function formatPrice (line 10) | function formatPrice(price: number | null | undefined): string {
  function CompanyDetailPage (line 16) | function CompanyDetailPage() {

FILE: competitor-analysis/app/competitors/page.tsx
  function generateId (line 23) | function generateId() {
  function CompetitorsPage (line 51) | function CompetitorsPage() {

FILE: competitor-analysis/app/dashboard/page.tsx
  type SortConfig (line 44) | interface SortConfig {
  function DashboardPage (line 49) | function DashboardPage() {

FILE: competitor-analysis/app/layout.tsx
  function RootLayout (line 27) | function RootLayout({

FILE: competitor-analysis/app/page.tsx
  function HomePage (line 7) | function HomePage() {

FILE: competitor-analysis/components/competitor-input.tsx
  type CompetitorRow (line 7) | interface CompetitorRow {
  type CompetitorInputProps (line 13) | interface CompetitorInputProps {
  constant SUGGESTED (line 19) | const SUGGESTED = ["Manus AI", "Devin AI", "Lindy AI", "GitHub Copilot",...
  function CompetitorInput (line 21) | function CompetitorInput({ onStartScraping, isLoading, existingCompetito...

FILE: competitor-analysis/components/dashboard-layout.tsx
  type NavItem (line 26) | interface NavItem {
  type DashboardLayoutProps (line 32) | interface DashboardLayoutProps {
  constant NAV_ITEMS (line 46) | const NAV_ITEMS: NavItem[] = [
  function DashboardLayout (line 54) | function DashboardLayout({

FILE: competitor-analysis/components/settings-panel.tsx
  type SettingsPanelProps (line 25) | interface SettingsPanelProps {
  function SettingsPanel (line 32) | function SettingsPanel({ open, onClose, baseline, onSave }: SettingsPane...

FILE: competitor-analysis/components/spreadsheet-view.tsx
  type SpreadsheetViewProps (line 19) | interface SpreadsheetViewProps {
  function InlineEditor (line 29) | function InlineEditor({
  function SpreadsheetView (line 140) | function SpreadsheetView({

FILE: competitor-analysis/components/ui/badge.tsx
  function Badge (line 28) | function Badge({

FILE: competitor-analysis/components/ui/button.tsx
  function Button (line 39) | function Button({

FILE: competitor-analysis/components/ui/card.tsx
  function Card (line 5) | function Card({ className, ...props }: React.ComponentProps<"div">) {
  function CardHeader (line 18) | function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
  function CardTitle (line 31) | function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
  function CardDescription (line 41) | function CardDescription({ className, ...props }: React.ComponentProps<"...
  function CardAction (line 51) | function CardAction({ className, ...props }: React.ComponentProps<"div">) {
  function CardContent (line 64) | function CardContent({ className, ...props }: React.ComponentProps<"div"...
  function CardFooter (line 74) | function CardFooter({ className, ...props }: React.ComponentProps<"div">) {

FILE: competitor-analysis/components/ui/dialog.tsx
  function Dialog (line 9) | function Dialog({
  function DialogTrigger (line 15) | function DialogTrigger({
  function DialogPortal (line 21) | function DialogPortal({
  function DialogClose (line 27) | function DialogClose({
  function DialogOverlay (line 33) | function DialogOverlay({
  function DialogContent (line 49) | function DialogContent({
  function DialogHeader (line 83) | function DialogHeader({ className, ...props }: React.ComponentProps<"div...
  function DialogFooter (line 93) | function DialogFooter({ className, ...props }: React.ComponentProps<"div...
  function DialogTitle (line 106) | function DialogTitle({
  function DialogDescription (line 119) | function DialogDescription({

FILE: competitor-analysis/components/ui/dot-pattern.tsx
  type DotPatternProps (line 5) | interface DotPatternProps extends React.SVGProps<SVGSVGElement> {
  function DotPattern (line 16) | function DotPattern({

FILE: competitor-analysis/components/ui/input.tsx
  function Input (line 5) | function Input({ className, type, ...props }: React.ComponentProps<"inpu...

FILE: competitor-analysis/components/ui/select.tsx
  function Select (line 9) | function Select({
  function SelectGroup (line 15) | function SelectGroup({
  function SelectValue (line 21) | function SelectValue({
  function SelectTrigger (line 27) | function SelectTrigger({
  function SelectContent (line 53) | function SelectContent({
  function SelectLabel (line 90) | function SelectLabel({
  function SelectItem (line 103) | function SelectItem({
  function SelectSeparator (line 130) | function SelectSeparator({
  function SelectScrollUpButton (line 143) | function SelectScrollUpButton({
  function SelectScrollDownButton (line 161) | function SelectScrollDownButton({

FILE: competitor-analysis/components/ui/separator.tsx
  function Separator (line 8) | function Separator({

FILE: competitor-analysis/components/ui/sheet.tsx
  function Sheet (line 9) | function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive....
  function SheetTrigger (line 13) | function SheetTrigger({
  function SheetClose (line 19) | function SheetClose({
  function SheetPortal (line 25) | function SheetPortal({
  function SheetOverlay (line 31) | function SheetOverlay({
  function SheetContent (line 47) | function SheetContent({
  function SheetHeader (line 84) | function SheetHeader({ className, ...props }: React.ComponentProps<"div"...
  function SheetFooter (line 94) | function SheetFooter({ className, ...props }: React.ComponentProps<"div"...
  function SheetTitle (line 104) | function SheetTitle({
  function SheetDescription (line 117) | function SheetDescription({

FILE: competitor-analysis/components/ui/sidebar.tsx
  constant SIDEBAR_COOKIE_NAME (line 28) | const SIDEBAR_COOKIE_NAME = "sidebar_state"
  constant SIDEBAR_COOKIE_MAX_AGE (line 29) | const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7
  constant SIDEBAR_WIDTH (line 30) | const SIDEBAR_WIDTH = "16rem"
  constant SIDEBAR_WIDTH_MOBILE (line 31) | const SIDEBAR_WIDTH_MOBILE = "18rem"
  constant SIDEBAR_WIDTH_ICON (line 32) | const SIDEBAR_WIDTH_ICON = "3rem"
  constant SIDEBAR_KEYBOARD_SHORTCUT (line 33) | const SIDEBAR_KEYBOARD_SHORTCUT = "b"
  type SidebarContextProps (line 35) | type SidebarContextProps = {
  function useSidebar (line 47) | function useSidebar() {
  function SidebarProvider (line 56) | function SidebarProvider({
  function Sidebar (line 154) | function Sidebar({
  function SidebarTrigger (line 256) | function SidebarTrigger({
  function SidebarRail (line 282) | function SidebarRail({ className, ...props }: React.ComponentProps<"butt...
  function SidebarInset (line 307) | function SidebarInset({ className, ...props }: React.ComponentProps<"mai...
  function SidebarInput (line 321) | function SidebarInput({
  function SidebarHeader (line 335) | function SidebarHeader({ className, ...props }: React.ComponentProps<"di...
  function SidebarFooter (line 346) | function SidebarFooter({ className, ...props }: React.ComponentProps<"di...
  function SidebarSeparator (line 357) | function SidebarSeparator({
  function SidebarContent (line 371) | function SidebarContent({ className, ...props }: React.ComponentProps<"d...
  function SidebarGroup (line 385) | function SidebarGroup({ className, ...props }: React.ComponentProps<"div...
  function SidebarGroupLabel (line 396) | function SidebarGroupLabel({
  function SidebarGroupAction (line 417) | function SidebarGroupAction({
  function SidebarGroupContent (line 440) | function SidebarGroupContent({
  function SidebarMenu (line 454) | function SidebarMenu({ className, ...props }: React.ComponentProps<"ul">) {
  function SidebarMenuItem (line 465) | function SidebarMenuItem({ className, ...props }: React.ComponentProps<"...
  function SidebarMenuButton (line 498) | function SidebarMenuButton({
  function SidebarMenuAction (line 548) | function SidebarMenuAction({
  function SidebarMenuBadge (line 580) | function SidebarMenuBadge({
  function SidebarMenuSkeleton (line 602) | function SidebarMenuSkeleton({
  function SidebarMenuSub (line 638) | function SidebarMenuSub({ className, ...props }: React.ComponentProps<"u...
  function SidebarMenuSubItem (line 653) | function SidebarMenuSubItem({
  function SidebarMenuSubButton (line 667) | function SidebarMenuSubButton({

FILE: competitor-analysis/components/ui/skeleton.tsx
  function Skeleton (line 3) | function Skeleton({ className, ...props }: React.ComponentProps<"div">) {

FILE: competitor-analysis/components/ui/tabs.tsx
  function Tabs (line 8) | function Tabs({
  function TabsList (line 21) | function TabsList({
  function TabsTrigger (line 37) | function TabsTrigger({
  function TabsContent (line 53) | function TabsContent({

FILE: competitor-analysis/components/ui/textarea.tsx
  function Textarea (line 5) | function Textarea({ className, ...props }: React.ComponentProps<"textare...

FILE: competitor-analysis/components/ui/tooltip.tsx
  function TooltipProvider (line 8) | function TooltipProvider({
  function Tooltip (line 21) | function Tooltip({
  function TooltipTrigger (line 31) | function TooltipTrigger({
  function TooltipContent (line 37) | function TooltipContent({

FILE: competitor-analysis/hooks/use-mobile.ts
  constant MOBILE_BREAKPOINT (line 3) | const MOBILE_BREAKPOINT = 768
  function useIsMobile (line 5) | function useIsMobile() {

FILE: competitor-analysis/lib/ai-client.ts
  function createOpenRouterProvider (line 6) | function createOpenRouterProvider() {
  function getModel (line 19) | function getModel(modelId: string = 'minimax/minimax-m2.1') {
  function generateStructured (line 25) | async function generateStructured<T>(
  function generateAIText (line 85) | async function generateAIText(

FILE: competitor-analysis/lib/ai-schemas.ts
  type UrlGenerationResult (line 12) | type UrlGenerationResult = z.infer<typeof urlGenerationSchema>;
  type PricingAnalysisResult (line 25) | type PricingAnalysisResult = z.infer<typeof pricingAnalysisSchema>;

FILE: competitor-analysis/lib/mino-client.ts
  constant MINO_API_URL (line 7) | const MINO_API_URL = "https://agent.tinyfish.ai/v1/automation/run-sse";
  type MinoRequestConfig (line 9) | interface MinoRequestConfig {
  type MinoResponse (line 19) | interface MinoResponse {
  function runMinoAutomation (line 34) | async function runMinoAutomation(
  function scrape (line 147) | async function scrape(

FILE: competitor-analysis/lib/pricing-context.tsx
  type Action (line 30) | type Action =
  function migrateOldSchema (line 51) | function migrateOldSchema(oldState: PricingState): PricingState {
  function reducer (line 125) | function reducer(state: PricingState, action: Action): PricingState {
  type PricingContextType (line 320) | interface PricingContextType {
  constant STORAGE_KEY (line 344) | const STORAGE_KEY = 'pricing-intelligence-state';
  function PricingProvider (line 346) | function PricingProvider({ children }: { children: ReactNode }) {
  function usePricing (line 400) | function usePricing() {

FILE: competitor-analysis/lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {
  type MinoEvent (line 9) | interface MinoEvent {
  function parseSSELine (line 22) | function parseSSELine(line: string): MinoEvent | null {
  function isCompleteEvent (line 37) | function isCompleteEvent(event: MinoEvent): boolean {
  function isErrorEvent (line 44) | function isErrorEvent(event: MinoEvent): boolean {
  function formatStepMessage (line 51) | function formatStepMessage(event: MinoEvent): string {

FILE: competitor-analysis/types/index.ts
  type BaselinePricing (line 1) | interface BaselinePricing {
  type Competitor (line 9) | interface Competitor {
  type PricingTier (line 19) | interface PricingTier {
  type CompetitorPricing (line 74) | interface CompetitorPricing {
  type EditHistory (line 106) | interface EditHistory {
  type CellEdit (line 115) | interface CellEdit {
  type VerificationAction (line 122) | interface VerificationAction {
  type ScrapingStatus (line 130) | interface ScrapingStatus {
  type NormalizedPricing (line 140) | interface NormalizedPricing {
  type Analysis (line 145) | interface Analysis {
  type DetailLevel (line 154) | type DetailLevel = 'low' | 'medium' | 'high';
  type PricingState (line 156) | interface PricingState {
  type SSEEventType (line 169) | type SSEEventType =
  type SSEEvent (line 181) | interface SSEEvent {
  type SpreadsheetRow (line 192) | interface SpreadsheetRow {

FILE: competitor-scout-cli/app/api/research/route.ts
  constant RATE_LIMIT_REQUESTS (line 14) | const RATE_LIMIT_REQUESTS = 25;
  constant RATE_LIMIT_WINDOW_MS (line 15) | const RATE_LIMIT_WINDOW_MS = 60_000;
  function getClientIp (line 22) | function getClientIp(request: NextRequest): string {
  function checkRateLimit (line 30) | function checkRateLimit(ip: string): { ok: boolean; retryAfter?: number } {
  function POST (line 57) | async function POST(request: NextRequest) {

FILE: competitor-scout-cli/app/layout.tsx
  function RootLayout (line 40) | function RootLayout({

FILE: competitor-scout-cli/app/page.tsx
  function Home (line 12) | function Home() {

FILE: competitor-scout-cli/cli/scout.mjs
  constant CONFIG_FILE (line 21) | const CONFIG_FILE = path.resolve(process.cwd(), ".scout.json");
  constant RUNS_FILE (line 22) | const RUNS_FILE = path.resolve(process.cwd(), ".scout-runs.json");
  constant RATE_LIMIT_FILE (line 23) | const RATE_LIMIT_FILE = path.resolve(process.cwd(), ".scout-ratelimit.js...
  constant TINYFISH_BASE (line 24) | const TINYFISH_BASE = "https://agent.tinyfish.ai/v1";
  constant ENV_FILE (line 25) | const ENV_FILE = path.resolve(process.cwd(), ".env.local");
  constant MAX_COMPETITORS (line 28) | const MAX_COMPETITORS = 10;
  constant RATE_LIMIT_REQUESTS (line 31) | const RATE_LIMIT_REQUESTS = 25;
  constant RATE_LIMIT_WINDOW_MS (line 32) | const RATE_LIMIT_WINDOW_MS = 60_000;
  function c (line 36) | function c(color, text) {
  function log (line 49) | function log(prefix, msg) {
  function info (line 54) | function info(msg) {
  function ok (line 57) | function ok(msg) {
  function warn (line 60) | function warn(msg) {
  function error (line 63) | function error(msg) {
  function loadConfig (line 67) | function loadConfig() {
  function normalizeQuotes (line 74) | function normalizeQuotes(value) {
  function saveConfig (line 83) | function saveConfig(config) {
  function loadRuns (line 87) | function loadRuns() {
  function saveRuns (line 94) | function saveRuns(runs) {
  function loadRateLimitHits (line 98) | function loadRateLimitHits() {
  function saveRateLimitHits (line 108) | function saveRateLimitHits(hits) {
  function checkRateLimit (line 117) | function checkRateLimit() {
  function recordRateLimitHit (line 129) | function recordRateLimitHit() {
  function loadEnvFile (line 135) | function loadEnvFile() {
  function requireEnv (line 157) | function requireEnv(name) {
  function tinyfishSubmit (line 168) | async function tinyfishSubmit(url, goal) {
  function tinyfishStatus (line 180) | async function tinyfishStatus(runId) {
  function tinyfishCancel (line 204) | async function tinyfishCancel(runId) {
  function tinyfishWait (line 214) | async function tinyfishWait(runId, label, onStatus) {
  function openaiChat (line 231) | async function openaiChat(messages, jsonMode = false) {
  function planGoals (line 253) | async function planGoals(competitors, question) {
  function summarizeResult (line 283) | async function summarizeResult(name, question, rawResult) {
  function generateReport (line 294) | async function generateReport(question, results) {
  function cmdInit (line 317) | function cmdInit() {
  function cmdClear (line 327) | function cmdClear() {
  function cmdReset (line 338) | function cmdReset() {
  function cmdAdd (line 348) | function cmdAdd(args) {
  function cmdList (line 372) | function cmdList() {
  function cmdRemove (line 388) | function cmdRemove(args) {
  function cmdResearch (line 408) | async function cmdResearch(args) {
  function cmdRuns (line 580) | function cmdRuns() {
  function cmdCancel (line 594) | async function cmdCancel(args) {
  function main (line 620) | async function main() {

FILE: competitor-scout-cli/components/cli-preview.tsx
  type CliPreviewProps (line 7) | interface CliPreviewProps {
  function CliPreview (line 13) | function CliPreview({ competitors, question, events }: CliPreviewProps) {

FILE: competitor-scout-cli/components/competitor-panel.tsx
  constant MAX_COMPETITORS (line 10) | const MAX_COMPETITORS = 10;
  type CompetitorPanelProps (line 12) | interface CompetitorPanelProps {
  function CompetitorPanel (line 18) | function CompetitorPanel({

FILE: competitor-scout-cli/components/event-log.tsx
  function getEventIcon (line 17) | function getEventIcon(type: ResearchEvent["type"]) {
  function getEventColor (line 42) | function getEventColor(type: ResearchEvent["type"]) {
  type EventLogProps (line 67) | interface EventLogProps {
  function EventLog (line 71) | function EventLog({ events }: EventLogProps) {

FILE: competitor-scout-cli/components/query-input.tsx
  type QueryInputProps (line 9) | interface QueryInputProps {
  function QueryInput (line 15) | function QueryInput({ onSubmit, disabled, isLoading }: QueryInputProps) {

FILE: competitor-scout-cli/components/report-view.tsx
  type ReportViewProps (line 10) | interface ReportViewProps {
  function MarkdownBlock (line 15) | function MarkdownBlock({ content }: { content: string }) {
  function renderInlineMarkdown (line 110) | function renderInlineMarkdown(text: string): React.ReactNode {
  type SourceLink (line 125) | type SourceLink = { url: string; title?: string };
  function extractSources (line 127) | function extractSources(raw: unknown): SourceLink[] {
  function formatSourceLabel (line 210) | function formatSourceLabel(url: string) {
  function SourcesButton (line 221) | function SourcesButton({ sources }: { sources: SourceLink[] }) {
  function RawJsonViewer (line 275) | function RawJsonViewer({ data }: { data: unknown }) {
  function ReportView (line 302) | function ReportView({ report, summaries }: ReportViewProps) {

FILE: competitor-scout-cli/lib/env.ts
  function parseEnvLine (line 6) | function parseEnvLine(line: string): { key: string; value: string } | nu...
  function ensureLocalEnvLoaded (line 22) | function ensureLocalEnvLoaded() {

FILE: competitor-scout-cli/lib/openai-client.ts
  function getApiKey (line 4) | function getApiKey(): string {
  function chatCompletion (line 18) | async function chatCompletion(
  function planResearchGoals (line 45) | async function planResearchGoals(
  function summarizeCompetitorResult (line 97) | async function summarizeCompetitorResult(
  function generateComparisonReport (line 120) | async function generateComparisonReport(

FILE: competitor-scout-cli/lib/tinyfish.ts
  constant TINYFISH_BASE_URL (line 3) | const TINYFISH_BASE_URL = "https://agent.tinyfish.ai/v1";
  function getApiKey (line 5) | function getApiKey(): string {
  function submitRun (line 12) | async function submitRun(
  function getRunStatus (line 34) | async function getRunStatus(runId: string): Promise<{
  function waitForCompletion (line 73) | async function waitForCompletion(

FILE: competitor-scout-cli/lib/types.ts
  type Competitor (line 1) | interface Competitor {
  type ResearchEvent (line 7) | interface ResearchEvent {
  type ResearchGoal (line 23) | interface ResearchGoal {
  type TinyfishRunResult (line 29) | interface TinyfishRunResult {
  type CompetitorResult (line 36) | interface CompetitorResult {

FILE: competitor-scout-cli/lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {

FILE: concept-discovery-system/src/App.tsx
  function AppContent (line 9) | function AppContent() {
  function App (line 47) | function App() {

FILE: concept-discovery-system/src/components/ErrorBoundary.tsx
  type ErrorBoundaryProps (line 4) | interface ErrorBoundaryProps {
  type ErrorBoundaryState (line 9) | interface ErrorBoundaryState {
  class ErrorBoundary (line 14) | class ErrorBoundary extends React.Component<
    method constructor (line 18) | constructor(props: ErrorBoundaryProps) {
    method getDerivedStateFromError (line 23) | static getDerivedStateFromError(error: Error): ErrorBoundaryState {
    method componentDidCatch (line 27) | componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    method render (line 31) | render() {

FILE: concept-discovery-system/src/components/input/InputForm.tsx
  function InputForm (line 6) | function InputForm() {

FILE: concept-discovery-system/src/components/layout/Footer.tsx
  function Footer (line 1) | function Footer() {

FILE: concept-discovery-system/src/components/layout/Header.tsx
  function Header (line 4) | function Header() {

FILE: concept-discovery-system/src/components/results/AnalysisPanel.tsx
  type AnalysisPanelProps (line 7) | interface AnalysisPanelProps {
  function getScoreBadge (line 12) | function getScoreBadge(score: number, type: 'competition' | 'validation'...
  function getOverallBadge (line 25) | function getOverallBadge(score: number) {
  function ScoreBar (line 32) | function ScoreBar({ label, score, type }: { label: string; score: number...
  function AnalysisPanel (line 58) | function AnalysisPanel({ userInput, projects }: AnalysisPanelProps) {
  function markdownToHtml (line 151) | function markdownToHtml(md: string): string {
  function inlineFormat (line 185) | function inlineFormat(text: string): string {

FILE: concept-discovery-system/src/components/results/ConceptCard.tsx
  type ConceptCardProps (line 5) | interface ConceptCardProps {
  function ConceptCard (line 16) | function ConceptCard({ data, onClick }: ConceptCardProps) {

FILE: concept-discovery-system/src/components/results/ConceptCardLoading.tsx
  type ConceptCardLoadingProps (line 7) | interface ConceptCardLoadingProps {
  function ConceptCardLoading (line 11) | function ConceptCardLoading({ agent }: ConceptCardLoadingProps) {

FILE: concept-discovery-system/src/components/results/Dashboard.tsx
  function Dashboard (line 5) | function Dashboard() {

FILE: concept-discovery-system/src/components/results/DetailPanel.tsx
  type DetailPanelProps (line 6) | interface DetailPanelProps {
  function DetailPanel (line 17) | function DetailPanel({ data, onClose }: DetailPanelProps) {

FILE: concept-discovery-system/src/components/results/ResultsGrid.tsx
  function MemoizedAnalysis (line 11) | function MemoizedAnalysis({ userInput, completed }: { userInput: string;...
  type ResultsGridProps (line 19) | interface ResultsGridProps {
  function ResultsGrid (line 25) | function ResultsGrid({ agents, phase, userInput }: ResultsGridProps) {

FILE: concept-discovery-system/src/context/DiscoveryContext.tsx
  function discoveryReducer (line 17) | function discoveryReducer(state: AppState, action: AppAction): AppState {
  function inferAgentStatus (line 179) | function inferAgentStatus(step: string): ConceptAgentState['status'] {
  function DiscoveryProvider (line 203) | function DiscoveryProvider({ children }: { children: ReactNode }) {
  function useDiscoveryContext (line 214) | function useDiscoveryContext() {

FILE: concept-discovery-system/src/hooks/useConceptDiscovery.ts
  function useConceptDiscovery (line 11) | function useConceptDiscovery() {

FILE: concept-discovery-system/src/lib/constants.ts
  constant TINYFISH_API_URL (line 2) | const TINYFISH_API_URL = 'https://agent.tinyfish.ai/v1/automation/run-sse';
  constant GITHUB_API_URL (line 3) | const GITHUB_API_URL = 'https://api.github.com';
  constant STACKEXCHANGE_API_URL (line 4) | const STACKEXCHANGE_API_URL = 'https://api.stackexchange.com/2.3';
  constant OPENROUTER_API_URL (line 5) | const OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/chat/completions';
  constant OPENROUTER_MODEL (line 8) | const OPENROUTER_MODEL = 'google/gemini-2.0-flash-001';
  constant OPENROUTER_TEMPERATURE (line 9) | const OPENROUTER_TEMPERATURE = 0.2;
  constant MAX_AGENTS (line 12) | const MAX_AGENTS = 10;
  constant AGENT_TIMEOUT (line 13) | const AGENT_TIMEOUT = 360000;
  constant MIN_INPUT_LENGTH (line 14) | const MIN_INPUT_LENGTH = 10;
  constant STACKOVERFLOW_RESULTS_PER_QUERY (line 15) | const STACKOVERFLOW_RESULTS_PER_QUERY = 3;
  constant APP_NAME (line 18) | const APP_NAME = 'Concept Discovery System';
  constant APP_DESCRIPTION (line 19) | const APP_DESCRIPTION =
  constant PLATFORM_INFO (line 23) | const PLATFORM_INFO = {

FILE: concept-discovery-system/src/lib/goal-builder.ts
  function buildAgentGoal (line 11) | function buildAgentGoal(
  function buildSOReasoningGoal (line 105) | function buildSOReasoningGoal(

FILE: concept-discovery-system/src/lib/openrouter-client.ts
  function extractJSON (line 4) | function extractJSON(text: string): unknown {
  function callOpenRouter (line 16) | async function callOpenRouter(systemPrompt: string, userPrompt: string):...
  type LLMQuery (line 47) | interface LLMQuery {
  function generateSmartQueries (line 58) | async function generateSmartQueries(userInput: string): Promise<SearchQu...
  type AnalysisResult (line 121) | interface AnalysisResult {
  function generateAnalysis (line 135) | async function generateAnalysis(

FILE: concept-discovery-system/src/lib/query-generator.ts
  constant STOPWORDS (line 6) | const STOPWORDS = new Set([
  function extractKeywords (line 15) | function extractKeywords(input: string): string[] {
  function getPrimaryTopic (line 26) | function getPrimaryTopic(keywords: string[]): string {
  function generateSearchQueries (line 34) | function generateSearchQueries(userInput: string): SearchQuery[] {

FILE: concept-discovery-system/src/lib/search-engines.ts
  function searchGitHub (line 7) | async function searchGitHub(
  function searchDevTo (line 62) | async function searchDevTo(query: string): Promise<SearchResult[]> {
  function searchStackOverflow (line 98) | async function searchStackOverflow(query: string, filters?: Record<strin...
  function executeSearches (line 179) | async function executeSearches(

FILE: concept-discovery-system/src/lib/tinyfish-client.ts
  function parseSSELine (line 7) | function parseSSELine(line: string): TinyFishSSEEvent | null {
  function startTinyFishAgent (line 21) | function startTinyFishAgent(

FILE: concept-discovery-system/src/lib/utils.ts
  function cn (line 8) | function cn(...inputs: ClassValue[]) {
  function formatTime (line 15) | function formatTime(timestamp: number): string {
  function getElapsedSeconds (line 28) | function getElapsedSeconds(startTime: number): number {
  function formatElapsedTime (line 35) | function formatElapsedTime(seconds: number): string {
  function generateAgentId (line 44) | function generateAgentId(platform: string): string {

FILE: concept-discovery-system/src/types/index.ts
  type Platform (line 2) | type Platform = 'github' | 'devto' | 'stackoverflow';
  type StackExchangeItem (line 5) | interface StackExchangeItem {
  type SearchQuery (line 17) | interface SearchQuery {
  type SearchResult (line 23) | interface SearchResult {
  type AgentStatus (line 36) | type AgentStatus =
  type AgentStep (line 43) | interface AgentStep {
  type ConceptAgentState (line 49) | interface ConceptAgentState {
  type ConceptData (line 64) | interface ConceptData {
  type AppPhase (line 81) | type AppPhase =
  type LogEntry (line 89) | interface LogEntry {
  type AppState (line 98) | interface AppState {
  type AppAction (line 110) | type AppAction =
  type TinyFishSSEEvent (line 126) | interface TinyFishSSEEvent {
  type TinyFishCallbacks (line 139) | interface TinyFishCallbacks {
  type TinyFishRequestConfig (line 146) | interface TinyFishRequestConfig {

FILE: fast-qa/app/api/execute-tests/route.ts
  type ExecuteTestsRequest (line 7) | interface ExecuteTestsRequest {
  function POST (line 14) | async function POST(request: NextRequest) {
  function executeTestCase (line 142) | async function executeTestCase(
  function buildGoalFromTestCase (line 338) | function buildGoalFromTestCase(testCase: TestCase): string {

FILE: fast-qa/app/api/generate-report/route.ts
  type GenerateReportRequest (line 5) | interface GenerateReportRequest {
  function POST (line 11) | async function POST(request: NextRequest) {

FILE: fast-qa/app/api/generate-tests/route.ts
  function createOpenRouterProvider (line 17) | function createOpenRouterProvider() {
  function POST (line 29) | async function POST(request: NextRequest) {

FILE: fast-qa/app/api/parse-test/route.ts
  function POST (line 4) | async function POST(request: NextRequest) {

FILE: fast-qa/app/layout.tsx
  function RootLayout (line 22) | function RootLayout({

FILE: fast-qa/app/page.tsx
  type TabType (line 43) | type TabType = 'projects' | 'tests' | 'execution' | 'history' | 'settings';
  type TestCreationMode (line 44) | type TestCreationMode = 'choice' | 'manual' | 'ai';
  function DashboardPage (line 46) | function DashboardPage() {

FILE: fast-qa/components/qa/ai-test-generator.tsx
  type AITestGeneratorProps (line 13) | interface AITestGeneratorProps {
  function AITestGenerator (line 18) | function AITestGenerator({ websiteUrl, onAddTests }: AITestGeneratorProp...

FILE: fast-qa/components/qa/dashboard-layout.tsx
  type DashboardLayoutProps (line 17) | interface DashboardLayoutProps {
  function DashboardLayout (line 32) | function DashboardLayout({

FILE: fast-qa/components/qa/project-card.tsx
  type ProjectCardProps (line 15) | interface ProjectCardProps {
  function ProjectCard (line 24) | function ProjectCard({

FILE: fast-qa/components/qa/project-dialog.tsx
  type ProjectDialogProps (line 19) | interface ProjectDialogProps {
  function ProjectDialog (line 26) | function ProjectDialog({

FILE: fast-qa/components/qa/settings-panel.tsx
  type SettingsPanelProps (line 19) | interface SettingsPanelProps {
  function SettingsPanel (line 25) | function SettingsPanel({

FILE: fast-qa/components/qa/test-case-detail.tsx
  type TestCaseDetailProps (line 24) | interface TestCaseDetailProps {
  function TestCaseDetail (line 31) | function TestCaseDetail({

FILE: fast-qa/components/qa/test-case-editor.tsx
  type TestCaseEditorProps (line 11) | interface TestCaseEditorProps {
  function TestCaseEditor (line 18) | function TestCaseEditor({

FILE: fast-qa/components/qa/test-case-list.tsx
  type TestCaseListProps (line 29) | interface TestCaseListProps {
  function TestCaseList (line 40) | function TestCaseList({

FILE: fast-qa/components/qa/test-execution-grid.tsx
  type TestExecutionCardProps (line 12) | interface TestExecutionCardProps {
  function TestExecutionCard (line 18) | function TestExecutionCard({ testCase, result, onSkip }: TestExecutionCa...
  type TestExecutionGridProps (line 176) | interface TestExecutionGridProps {
  function TestExecutionGrid (line 183) | function TestExecutionGrid({

FILE: fast-qa/components/qa/test-results-table.tsx
  type TestResultsTableProps (line 31) | interface TestResultsTableProps {
  function TestResultsTable (line 37) | function TestResultsTable({

FILE: fast-qa/components/ui/accordion.tsx
  function Accordion (line 9) | function Accordion({
  function AccordionItem (line 15) | function AccordionItem({
  function AccordionTrigger (line 28) | function AccordionTrigger({
  function AccordionContent (line 50) | function AccordionContent({

FILE: fast-qa/components/ui/alert-dialog.tsx
  function AlertDialog (line 9) | function AlertDialog({
  function AlertDialogTrigger (line 15) | function AlertDialogTrigger({
  function AlertDialogPortal (line 23) | function AlertDialogPortal({
  function AlertDialogOverlay (line 31) | function AlertDialogOverlay({
  function AlertDialogContent (line 47) | function AlertDialogContent({
  function AlertDialogHeader (line 66) | function AlertDialogHeader({
  function AlertDialogFooter (line 79) | function AlertDialogFooter({
  function AlertDialogTitle (line 95) | function AlertDialogTitle({
  function AlertDialogDescription (line 108) | function AlertDialogDescription({
  function AlertDialogAction (line 121) | function AlertDialogAction({
  function AlertDialogCancel (line 133) | function AlertDialogCancel({

FILE: fast-qa/components/ui/alert.tsx
  function Alert (line 22) | function Alert({
  function AlertTitle (line 37) | function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
  function AlertDescription (line 50) | function AlertDescription({

FILE: fast-qa/components/ui/badge.tsx
  function Badge (line 28) | function Badge({

FILE: fast-qa/components/ui/button.tsx
  function Button (line 39) | function Button({

FILE: fast-qa/components/ui/card.tsx
  function Card (line 5) | function Card({ className, ...props }: React.ComponentProps<"div">) {
  function CardHeader (line 18) | function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
  function CardTitle (line 31) | function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
  function CardDescription (line 41) | function CardDescription({ className, ...props }: React.ComponentProps<"...
  function CardAction (line 51) | function CardAction({ className, ...props }: React.ComponentProps<"div">) {
  function CardContent (line 64) | function CardContent({ className, ...props }: React.ComponentProps<"div"...
  function CardFooter (line 74) | function CardFooter({ className, ...props }: React.ComponentProps<"div">) {

FILE: fast-qa/components/ui/checkbox.tsx
  function Checkbox (line 9) | function Checkbox({

FILE: fast-qa/components/ui/collapsible.tsx
  function Collapsible (line 5) | function Collapsible({
  function CollapsibleTrigger (line 11) | function CollapsibleTrigger({
  function CollapsibleContent (line 22) | function CollapsibleContent({

FILE: fast-qa/components/ui/dialog.tsx
  function Dialog (line 9) | function Dialog({
  function DialogTrigger (line 15) | function DialogTrigger({
  function DialogPortal (line 21) | function DialogPortal({
  function DialogClose (line 27) | function DialogClose({
  function DialogOverlay (line 33) | function DialogOverlay({
  function DialogContent (line 49) | function DialogContent({
  function DialogHeader (line 83) | function DialogHeader({ className, ...props }: React.ComponentProps<"div...
  function DialogFooter (line 93) | function DialogFooter({ className, ...props }: React.ComponentProps<"div...
  function DialogTitle (line 106) | function DialogTitle({
  function DialogDescription (line 119) | function DialogDescription({

FILE: fast-qa/components/ui/dropdown-menu.tsx
  function DropdownMenu (line 9) | function DropdownMenu({
  function DropdownMenuPortal (line 15) | function DropdownMenuPortal({
  function DropdownMenuTrigger (line 23) | function DropdownMenuTrigger({
  function DropdownMenuContent (line 34) | function DropdownMenuContent({
  function DropdownMenuGroup (line 54) | function DropdownMenuGroup({
  function DropdownMenuItem (line 62) | function DropdownMenuItem({
  function DropdownMenuCheckboxItem (line 85) | function DropdownMenuCheckboxItem({
  function DropdownMenuRadioGroup (line 111) | function DropdownMenuRadioGroup({
  function DropdownMenuRadioItem (line 122) | function DropdownMenuRadioItem({
  function DropdownMenuLabel (line 146) | function DropdownMenuLabel({
  function DropdownMenuSeparator (line 166) | function DropdownMenuSeparator({
  function DropdownMenuShortcut (line 179) | function DropdownMenuShortcut({
  function DropdownMenuSub (line 195) | function DropdownMenuSub({
  function DropdownMenuSubTrigger (line 201) | function DropdownMenuSubTrigger({
  function DropdownMenuSubContent (line 225) | function DropdownMenuSubContent({

FILE: fast-qa/components/ui/form.tsx
  type FormFieldContextValue (line 21) | type FormFieldContextValue<
  type FormItemContextValue (line 68) | type FormItemContextValue = {
  function FormItem (line 76) | function FormItem({ className, ...props }: React.ComponentProps<"div">) {
  function FormLabel (line 90) | function FormLabel({
  function FormControl (line 107) | function FormControl({ ...props }: React.ComponentProps<typeof Slot>) {
  function FormDescription (line 125) | function FormDescription({ className, ...props }: React.ComponentProps<"...
  function FormMessage (line 138) | function FormMessage({ className, ...props }: React.ComponentProps<"p">) {

FILE: fast-qa/components/ui/input.tsx
  function Input (line 5) | function Input({ className, type, ...props }: React.ComponentProps<"inpu...

FILE: fast-qa/components/ui/label.tsx
  function Label (line 8) | function Label({

FILE: fast-qa/components/ui/progress.tsx
  function Progress (line 8) | function Progress({

FILE: fast-qa/components/ui/select.tsx
  function Select (line 9) | function Select({
  function SelectGroup (line 15) | function SelectGroup({
  function SelectValue (line 21) | function SelectValue({
  function SelectTrigger (line 27) | function SelectTrigger({
  function SelectContent (line 53) | function SelectContent({
  function SelectLabel (line 90) | function SelectLabel({
  function SelectItem (line 103) | function SelectItem({
  function SelectSeparator (line 130) | function SelectSeparator({
  function SelectScrollUpButton (line 143) | function SelectScrollUpButton({
  function SelectScrollDownButton (line 161) | function SelectScrollDownButton({

FILE: fast-qa/components/ui/separator.tsx
  function Separator (line 8) | function Separator({

FILE: fast-qa/components/ui/sheet.tsx
  function Sheet (line 9) | function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive....
  function SheetTrigger (line 13) | function SheetTrigger({
  function SheetClose (line 19) | function SheetClose({
  function SheetPortal (line 25) | function SheetPortal({
  function SheetOverlay (line 31) | function SheetOverlay({
  function SheetContent (line 47) | function SheetContent({
  function SheetHeader (line 84) | function SheetHeader({ className, ...props }: React.ComponentProps<"div"...
  function SheetFooter (line 94) | function SheetFooter({ className, ...props }: React.ComponentProps<"div"...
  function SheetTitle (line 104) | function SheetTitle({
  function SheetDescription (line 117) | function SheetDescription({

FILE: fast-qa/components/ui/skeleton.tsx
  function Skeleton (line 3) | function Skeleton({ className, ...props }: React.ComponentProps<"div">) {

FILE: fast-qa/components/ui/switch.tsx
  function Switch (line 8) | function Switch({

FILE: fast-qa/components/ui/table.tsx
  function Table (line 7) | function Table({ className, ...props }: React.ComponentProps<"table">) {
  function TableHeader (line 22) | function TableHeader({ className, ...props }: React.ComponentProps<"thea...
  function TableBody (line 32) | function TableBody({ className, ...props }: React.ComponentProps<"tbody"...
  function TableFooter (line 42) | function TableFooter({ className, ...props }: React.ComponentProps<"tfoo...
  function TableRow (line 55) | function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
  function TableHead (line 68) | function TableHead({ className, ...props }: React.ComponentProps<"th">) {
  function TableCell (line 81) | function TableCell({ className, ...props }: React.ComponentProps<"td">) {
  function TableCaption (line 94) | function TableCaption({

FILE: fast-qa/components/ui/tabs.tsx
  function Tabs (line 8) | function Tabs({
  function TabsList (line 21) | function TabsList({
  function TabsTrigger (line 37) | function TabsTrigger({
  function TabsContent (line 53) | function TabsContent({

FILE: fast-qa/components/ui/textarea.tsx
  function Textarea (line 5) | function Textarea({ className, ...props }: React.ComponentProps<"textare...

FILE: fast-qa/components/ui/tooltip.tsx
  function TooltipProvider (line 8) | function TooltipProvider({
  function Tooltip (line 21) | function Tooltip({
  function TooltipTrigger (line 31) | function TooltipTrigger({
  function TooltipContent (line 37) | function TooltipContent({

FILE: fast-qa/lib/ai-client.ts
  function createOpenRouterProvider (line 6) | function createOpenRouterProvider() {
  function getModel (line 19) | function getModel(modelId: string = 'minimax/minimax-m2.1') {
  type ParseTestResponse (line 53) | type ParseTestResponse = z.infer<typeof parseTestSchema>;
  type BugReport (line 54) | type BugReport = z.infer<typeof bugReportSchema>;
  function parseTestDescription (line 59) | async function parseTestDescription(
  function generateBugReport (line 125) | async function generateBugReport(
  function generateAIText (line 235) | async function generateAIText(
  function generateTestResultSummary (line 256) | async function generateTestResultSummary(

FILE: fast-qa/lib/hooks.ts
  function useLocalStorage (line 10) | function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T...
  function useTestExecution (line 46) | function useTestExecution(onComplete?: (finalResults: Map<string, TestRe...
  function useDebounce (line 231) | function useDebounce<T>(value: T, delay: number): T {
  function useWindowSize (line 250) | function useWindowSize() {
  function useKeyboardShortcut (line 275) | function useKeyboardShortcut(
  function useClickOutside (line 311) | function useClickOutside(
  function useElapsedTime (line 330) | function useElapsedTime(startTime: number | null, isRunning: boolean): n...

FILE: fast-qa/lib/mino-client.ts
  constant MINO_API_URL (line 7) | const MINO_API_URL = "https://agent.tinyfish.ai/v1/automation/run-sse";
  type MinoRequestConfig (line 9) | interface MinoRequestConfig {
  type MinoResponse (line 19) | interface MinoResponse {
  type MinoStreamCallbacks (line 27) | interface MinoStreamCallbacks {
  function runMinoAutomation (line 37) | async function runMinoAutomation(
  function buildGoalFromSteps (line 146) | function buildGoalFromSteps(
  function buildGoalFromDescription (line 181) | function buildGoalFromDescription(
  function scrape (line 205) | async function scrape(

FILE: fast-qa/lib/qa-context.tsx
  function reducer (line 34) | function reducer(state: QAState, action: QAAction): QAState {
  type QAContextType (line 271) | interface QAContextType {
  constant STORAGE_KEY (line 299) | const STORAGE_KEY = 'qa-tester-state';
  function QAProvider (line 301) | function QAProvider({ children }: { children: ReactNode }) {
  function useQA (line 486) | function useQA() {

FILE: fast-qa/lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {
  function generateId (line 9) | function generateId(): string {
  function formatDuration (line 14) | function formatDuration(ms: number): string {
  function formatRelativeTime (line 23) | function formatRelativeTime(timestamp: number): string {
  type MinoEvent (line 35) | interface MinoEvent {
  function parseSSELine (line 47) | function parseSSELine(line: string): MinoEvent | null {
  function isCompleteEvent (line 61) | function isCompleteEvent(event: MinoEvent): boolean {
  function isErrorEvent (line 65) | function isErrorEvent(event: MinoEvent): boolean {
  function formatStepMessage (line 69) | function formatStepMessage(event: MinoEvent): string {
  function isValidUrl (line 86) | function isValidUrl(string: string): boolean {
  function truncate (line 96) | function truncate(text: string, maxLength: number): string {
  function debounce (line 102) | function debounce<T extends (...args: unknown[]) => unknown>(
  function getStatusColor (line 114) | function getStatusColor(status: string): string {
  function getStatusBgColor (line 131) | function getStatusBgColor(status: string): string {

FILE: fast-qa/types/index.ts
  type Project (line 2) | interface Project {
  type TestStatus (line 14) | type TestStatus = 'pending' | 'running' | 'passed' | 'failed' | 'skipped';
  type TestCase (line 16) | interface TestCase {
  type TestResult (line 28) | interface TestResult {
  type TestRun (line 46) | interface TestRun {
  type QASettings (line 60) | interface QASettings {
  type GeneratedTest (line 69) | interface GeneratedTest {
  type BulkGenerateRequest (line 75) | interface BulkGenerateRequest {
  type BulkGenerateResponse (line 80) | interface BulkGenerateResponse {
  type TestEventType (line 85) | type TestEventType =
  type TestEvent (line 94) | interface TestEvent {
  type AllCompleteEvent (line 109) | interface AllCompleteEvent {
  type BugReport (line 122) | interface BugReport {
  type QAState (line 134) | interface QAState {
  type QAAction (line 146) | type QAAction =

FILE: game-buying-guide/app/api/analyze-platform/route.ts
  constant MINO_API_KEY (line 6) | const MINO_API_KEY = process.env.MINO_API_KEY
  function POST (line 8) | async function POST(request: Request) {

FILE: game-buying-guide/app/api/discover-platforms/route.ts
  constant GAMING_PLATFORMS (line 4) | const GAMING_PLATFORMS = [
  function POST (line 57) | async function POST(request: Request) {

FILE: game-buying-guide/app/api/steamdb-price-history/route.ts
  constant MINO_API_KEY (line 6) | const MINO_API_KEY = process.env.MINO_API_KEY
  function POST (line 8) | async function POST(request: Request) {

FILE: game-buying-guide/app/layout.tsx
  function RootLayout (line 33) | function RootLayout({

FILE: game-buying-guide/app/loading.tsx
  function Loading (line 1) | function Loading() {

FILE: game-buying-guide/app/page.tsx
  function HomePage (line 12) | function HomePage() {

FILE: game-buying-guide/components/agent-card.tsx
  type AgentCardProps (line 23) | interface AgentCardProps {
  function AgentCard (line 28) | function AgentCard({ agent, onExpandPreview }: AgentCardProps) {

FILE: game-buying-guide/components/agent-grid.tsx
  type AgentGridProps (line 9) | interface AgentGridProps {
  function AgentGrid (line 13) | function AgentGrid({ agents }: AgentGridProps) {

FILE: game-buying-guide/components/live-browser-preview.tsx
  type LiveBrowserPreviewProps (line 9) | interface LiveBrowserPreviewProps {
  function LiveBrowserPreview (line 15) | function LiveBrowserPreview({ streamingUrl, platformName, onClose }: Liv...

FILE: game-buying-guide/components/results-summary.tsx
  type ResultsSummaryProps (line 10) | interface ResultsSummaryProps {
  function ResultsSummary (line 15) | function ResultsSummary({ agents, gameName }: ResultsSummaryProps) {

FILE: game-buying-guide/components/search-form.tsx
  type SearchFormProps (line 10) | interface SearchFormProps {
  function SearchForm (line 15) | function SearchForm({ onSearch, isLoading }: SearchFormProps) {

FILE: game-buying-guide/components/steamdb-price-card.tsx
  type SteamDBPriceCardProps (line 9) | interface SteamDBPriceCardProps {
  function SteamDBPriceCard (line 14) | function SteamDBPriceCard({ agent, gameName }: SteamDBPriceCardProps) {

FILE: game-buying-guide/components/theme-provider.tsx
  function ThemeProvider (line 9) | function ThemeProvider({ children, ...props }: ThemeProviderProps) {

FILE: game-buying-guide/components/ui/accordion.tsx
  function Accordion (line 9) | function Accordion({
  function AccordionItem (line 15) | function AccordionItem({
  function AccordionTrigger (line 28) | function AccordionTrigger({
  function AccordionContent (line 50) | function AccordionContent({

FILE: game-buying-guide/components/ui/alert-dialog.tsx
  function AlertDialog (line 9) | function AlertDialog({
  function AlertDialogTrigger (line 15) | function AlertDialogTrigger({
  function AlertDialogPortal (line 23) | function AlertDialogPortal({
  function AlertDialogOverlay (line 31) | function AlertDialogOverlay({
  function AlertDialogContent (line 47) | function AlertDialogContent({
  function AlertDialogHeader (line 66) | function AlertDialogHeader({
  function AlertDialogFooter (line 79) | function AlertDialogFooter({
  function AlertDialogTitle (line 95) | function AlertDialogTitle({
  function AlertDialogDescription (line 108) | function AlertDialogDescription({
  function AlertDialogAction (line 121) | function AlertDialogAction({
  function AlertDialogCancel (line 133) | function AlertDialogCancel({

FILE: game-buying-guide/components/ui/alert.tsx
  function Alert (line 22) | function Alert({
  function AlertTitle (line 37) | function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) {
  function AlertDescription (line 50) | function AlertDescription({

FILE: game-buying-guide/components/ui/aspect-ratio.tsx
  function AspectRatio (line 5) | function AspectRatio({

FILE: game-buying-guide/components/ui/avatar.tsx
  function Avatar (line 8) | function Avatar({
  function AvatarImage (line 24) | function AvatarImage({
  function AvatarFallback (line 37) | function AvatarFallback({

FILE: game-buying-guide/components/ui/badge.tsx
  function Badge (line 28) | function Badge({

FILE: game-buying-guide/components/ui/breadcrumb.tsx
  function Breadcrumb (line 7) | function Breadcrumb({ ...props }: React.ComponentProps<'nav'>) {
  function BreadcrumbList (line 11) | function BreadcrumbList({ className, ...props }: React.ComponentProps<'o...
  function BreadcrumbItem (line 24) | function BreadcrumbItem({ className, ...props }: React.ComponentProps<'l...
  function BreadcrumbLink (line 34) | function BreadcrumbLink({
  function BreadcrumbPage (line 52) | function BreadcrumbPage({ className, ...props }: React.ComponentProps<'s...
  function BreadcrumbSeparator (line 65) | function BreadcrumbSeparator({
  function BreadcrumbEllipsis (line 83) | function BreadcrumbEllipsis({

FILE: game-buying-guide/components/ui/button-group.tsx
  function ButtonGroup (line 24) | function ButtonGroup({
  function ButtonGroupText (line 40) | function ButtonGroupText({
  function ButtonGroupSeparator (line 60) | function ButtonGroupSeparator({

FILE: game-buying-guide/components/ui/button.tsx
  function Button (line 39) | function Button({

FILE: game-buying-guide/components/ui/calendar.tsx
  function Calendar (line 14) | function Calendar({
  function CalendarDayButton (line 175) | function CalendarDayButton({

FILE: game-buying-guide/components/ui/card.tsx
  function Card (line 5) | function Card({ className, ...props }: React.ComponentProps<'div'>) {
  function CardHeader (line 18) | function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
  function CardTitle (line 31) | function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
  function CardDescription (line 41) | function CardDescription({ className, ...props }: React.ComponentProps<'...
  function CardAction (line 51) | function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
  function CardContent (line 64) | function CardContent({ className, ...props }: React.ComponentProps<'div'...
  function CardFooter (line 74) | function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {

FILE: game-buying-guide/components/ui/carousel.tsx
  type CarouselApi (line 12) | type CarouselApi = UseEmblaCarouselType[1]
  type UseCarouselParameters (line 13) | type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
  type CarouselOptions (line 14) | type CarouselOptions = UseCarouselParameters[0]
  type CarouselPlugin (line 15) | type CarouselPlugin = UseCarouselParameters[1]
  type CarouselProps (line 17) | type CarouselProps = {
  type CarouselContextProps (line 24) | type CarouselContextProps = {
  function useCarousel (line 35) | function useCarousel() {
  function Carousel (line 45) | function Carousel({
  function CarouselContent (line 135) | function CarouselContent({ className, ...props }: React.ComponentProps<'...
  function CarouselItem (line 156) | function CarouselItem({ className, ...props }: React.ComponentProps<'div...
  function CarouselPrevious (line 174) | function CarouselPrevious({
  function CarouselNext (line 204) | function CarouselNext({

FILE: game-buying-guide/components/ui/chart.tsx
  constant THEMES (line 9) | const THEMES = { light: '', dark: '.dark' } as const
  type ChartConfig (line 11) | type ChartConfig = {
  type ChartContextProps (line 21) | type ChartContextProps = {
  function useChart (line 27) | function useChart() {
  function ChartContainer (line 37) | function ChartContainer({
  function ChartTooltipContent (line 107) | function ChartTooltipContent({
  function ChartLegendContent (line 253) | function ChartLegendContent({
  function getPayloadConfigFromPayload (line 308) | function getPayloadConfigFromPayload(

FILE: game-buying-guide/components/ui/checkbox.tsx
  function Checkbox (line 9) | function Checkbox({

FILE: game-buying-guide/components/ui/collapsible.tsx
  function Collapsible (line 6) | function Collapsible({
  function CollapsibleTrigger (line 12) | function CollapsibleTrigger({
  function CollapsibleContent (line 23) | function CollapsibleContent({

FILE: game-buying-guide/components/ui/command.tsx
  function Command (line 16) | function Command({
  function CommandDialog (line 32) | function CommandDialog({
  function CommandInput (line 63) | function CommandInput({
  function CommandList (line 85) | function CommandList({
  function CommandEmpty (line 101) | function CommandEmpty({
  function CommandGroup (line 113) | function CommandGroup({
  function CommandSeparator (line 129) | function CommandSeparator({
  function CommandItem (line 142) | function CommandItem({
  function CommandShortcut (line 158) | function CommandShortcut({

FILE: game-buying-guide/components/ui/context-menu.tsx
  function ContextMenu (line 9) | function ContextMenu({
  function ContextMenuTrigger (line 15) | function ContextMenuTrigger({
  function ContextMenuGroup (line 23) | function ContextMenuGroup({
  function ContextMenuPortal (line 31) | function ContextMenuPortal({
  function ContextMenuSub (line 39) | function ContextMenuSub({
  function ContextMenuRadioGroup (line 45) | function ContextMenuRadioGroup({
  function ContextMenuSubTrigger (line 56) | function ContextMenuSubTrigger({
  function ContextMenuSubContent (line 80) | function ContextMenuSubContent({
  function ContextMenuContent (line 96) | function ContextMenuContent({
  function ContextMenuItem (line 114) | function ContextMenuItem({
  function ContextMenuCheckboxItem (line 137) | function ContextMenuCheckboxItem({
  function ContextMenuRadioItem (line 163) | function ContextMenuRadioItem({
  function ContextMenuLabel (line 187) | function ContextMenuLabel({
  function ContextMenuSeparator (line 207) | function ContextMenuSeparator({
  function ContextMenuShortcut (line 220) | function ContextMenuShortcut({

FILE: game-buying-guide/components/ui/dialog.tsx
  function Dialog (line 9) | function Dialog({
  function DialogTrigger (line 15) | function DialogTrigger({
  function DialogPortal (line 21) | function DialogPortal({
  function DialogClose (line 27) | function DialogClose({
  function DialogOverlay (line 33) | function DialogOverlay({
  function DialogContent (line 49) | function DialogContent({
  function DialogHeader (line 83) | function DialogHeader({ className, ...props }: React.ComponentProps<'div...
  function DialogFooter (line 93) | function DialogFooter({ className, ...props }: React.ComponentProps<'div...
  function DialogTitle (line 106) | function DialogTitle({
  function DialogDescription (line 119) | function DialogDescription({

FILE: game-buying-guide/components/ui/drawer.tsx
  function Drawer (line 8) | function Drawer({
  function DrawerTrigger (line 14) | function DrawerTrigger({
  function DrawerPortal (line 20) | function DrawerPortal({
  function DrawerClose (line 26) | function DrawerClose({
  function DrawerOverlay (line 32) | function DrawerOverlay({
  function DrawerContent (line 48) | function DrawerContent({
  function DrawerHeader (line 75) | function DrawerHeader({ className, ...props }: React.ComponentProps<'div...
  function DrawerFooter (line 88) | function DrawerFooter({ className, ...props }: React.ComponentProps<'div...
  function DrawerTitle (line 98) | function DrawerTitle({
  function DrawerDescription (line 111) | function DrawerDescription({

FILE: game-buying-guide/components/ui/dropdown-menu.tsx
  function DropdownMenu (line 9) | function DropdownMenu({
  function DropdownMenuPortal (line 15) | function DropdownMenuPortal({
  function DropdownMenuTrigger (line 23) | function DropdownMenuTrigger({
  function DropdownMenuContent (line 34) | function DropdownMenuContent({
  function DropdownMenuGroup (line 54) | function DropdownMenuGroup({
  function DropdownMenuItem (line 62) | function DropdownMenuItem({
  function DropdownMenuCheckboxItem (line 85) | function DropdownMenuCheckboxItem({
  function DropdownMenuRadioGroup (line 111) | function DropdownMenuRadioGroup({
  function DropdownMenuRadioItem (line 122) | function DropdownMenuRadioItem({
  function DropdownMenuLabel (line 146) | function DropdownMenuLabel({
  function DropdownMenuSeparator (line 166) | function DropdownMenuSeparator({
  function DropdownMenuShortcut (line 179) | function DropdownMenuShortcut({
  function DropdownMenuSub (line 195) | function DropdownMenuSub({
  function DropdownMenuSubTrigger (line 201) | function DropdownMenuSubTrigger({
  function DropdownMenuSubContent (line 225) | function DropdownMenuSubContent({

FILE: game-buying-guide/components/ui/empty.tsx
  function Empty (line 5) | function Empty({ className, ...props }: React.ComponentProps<'div'>) {
  function EmptyHeader (line 18) | function EmptyHeader({ className, ...props }: React.ComponentProps<'div'...
  function EmptyMedia (line 46) | function EmptyMedia({
  function EmptyTitle (line 61) | function EmptyTitle({ className, ...props }: React.ComponentProps<'div'>) {
  function EmptyDescription (line 71) | function EmptyDescription({ className, ...props }: React.ComponentProps<...
  function EmptyContent (line 84) | function EmptyContent({ className, ...props }: React.ComponentProps<'div...

FILE: game-buying-guide/components/ui/field.tsx
  function FieldSet (line 10) | function FieldSet({ className, ...props }: React.ComponentProps<'fieldse...
  function FieldLegend (line 24) | function FieldLegend({
  function FieldGroup (line 44) | function FieldGroup({ className, ...props }: React.ComponentProps<'div'>) {
  function Field (line 81) | function Field({
  function FieldContent (line 97) | function FieldContent({ className, ...props }: React.ComponentProps<'div...
  function FieldLabel (line 110) | function FieldLabel({
  function FieldTitle (line 128) | function FieldTitle({ className, ...props }: React.ComponentProps<'div'>) {
  function FieldDescription (line 141) | function FieldDescription({ className, ...props }: React.ComponentProps<...
  function FieldSeparator (line 156) | function FieldSeparator({
  function FieldError (line 186) | function FieldError({

FILE: game-buying-guide/components/ui/form.tsx
  type FormFieldContextValue (line 21) | type FormFieldContextValue<
  type FormItemContextValue (line 68) | type FormItemContextValue = {
  function FormItem (line 76) | function FormItem({ className, ...props }: React.ComponentProps<'div'>) {
  function FormLabel (line 90) | function FormLabel({
  function FormControl (line 107) | function FormControl({ ...props }: React.ComponentProps<typeof Slot>) {
  function FormDescription (line 125) | function FormDescription({ className, ...props }: React.ComponentProps<'...
  function FormMessage (line 138) | function FormMessage({ className, ...props }: React.ComponentProps<'p'>) {

FILE: game-buying-guide/components/ui/hover-card.tsx
  function HoverCard (line 8) | function HoverCard({
  function HoverCardTrigger (line 14) | function HoverCardTrigger({
  function HoverCardContent (line 22) | function HoverCardContent({

FILE: game-buying-guide/components/ui/input-group.tsx
  function InputGroup (line 10) | function InputGroup({ className, ...props }: React.ComponentProps<'div'>) {
  function InputGroupAddon (line 59) | function InputGroupAddon({
  function InputGroupButton (line 99) | function InputGroupButton({
  function InputGroupText (line 118) | function InputGroupText({ className, ...props }: React.ComponentProps<'s...
  function InputGroupInput (line 130) | function InputGroupInput({
  function InputGroupTextarea (line 146) | function InputGroupTextarea({

FILE: game-buying-guide/components/ui/input-otp.tsx
  function InputOTP (line 9) | function InputOTP({
  function InputOTPGroup (line 29) | function InputOTPGroup({ className, ...props }: React.ComponentProps<'di...
  function InputOTPSlot (line 39) | function InputOTPSlot({
  function InputOTPSeparator (line 69) | function InputOTPSeparator({ ...props }: React.ComponentProps<'div'>) {

FILE: game-buying-guide/components/ui/input.tsx
  function Input (line 5) | function Input({ className, type, ...props }: React.ComponentProps<'inpu...

FILE: game-buying-guide/components/ui/item.tsx
  function ItemGroup (line 8) | function ItemGroup({ className, ...props }: React.ComponentProps<'div'>) {
  function ItemSeparator (line 19) | function ItemSeparator({
  function Item (line 54) | function Item({
  function ItemMedia (line 91) | function ItemMedia({
  function ItemContent (line 106) | function ItemContent({ className, ...props }: React.ComponentProps<'div'...
  function ItemTitle (line 119) | function ItemTitle({ className, ...props }: React.ComponentProps<'div'>) {
  function ItemDescription (line 132) | function ItemDescription({ className, ...props }: React.ComponentProps<'...
  function ItemActions (line 146) | function ItemActions({ className, ...props }: React.ComponentProps<'div'...
  function ItemHeader (line 156) | function ItemHeader({ className, ...props }: React.ComponentProps<'div'>) {
  function ItemFooter (line 169) | function ItemFooter({ className, ...props }: React.ComponentProps<'div'>) {

FILE: game-buying-guide/components/ui/kbd.tsx
  function Kbd (line 3) | function Kbd({ className, ...props }: React.ComponentProps<'kbd'>) {
  function KbdGroup (line 18) | function KbdGroup({ className, ...props }: React.ComponentProps<'div'>) {

FILE: game-buying-guide/components/ui/label.tsx
  function Label (line 8) | function Label({

FILE: game-buying-guide/components/ui/menubar.tsx
  function Menubar (line 9) | function Menubar({
  function MenubarMenu (line 25) | function MenubarMenu({
  function MenubarGroup (line 31) | function MenubarGroup({
  function MenubarPortal (line 37) | function MenubarPortal({
  function MenubarRadioGroup (line 43) | function MenubarRadioGroup({
  function MenubarTrigger (line 51) | function MenubarTrigger({
  function MenubarContent (line 67) | function MenubarContent({
  function MenubarItem (line 91) | function MenubarItem({
  function MenubarCheckboxItem (line 114) | function MenubarCheckboxItem({
  function MenubarRadioItem (line 140) | function MenubarRadioItem({
  function MenubarLabel (line 164) | function MenubarLabel({
  function MenubarSeparator (line 184) | function MenubarSeparator({
  function MenubarShortcut (line 197) | function MenubarShortcut({
  function MenubarSub (line 213) | function MenubarSub({
  function MenubarSubTrigger (line 219) | function MenubarSubTrigger({
  function MenubarSubContent (line 243) | function MenubarSubContent({

FILE: game-buying-guide/components/ui/navigation-menu.tsx
  function NavigationMenu (line 8) | function NavigationMenu({
  function NavigationMenuList (line 32) | function NavigationMenuList({
  function NavigationMenuItem (line 48) | function NavigationMenuItem({
  function NavigationMenuTrigger (line 65) | function NavigationMenuTrigger({
  function NavigationMenuContent (line 85) | function NavigationMenuContent({
  function NavigationMenuViewport (line 102) | function NavigationMenuViewport({
  function NavigationMenuLink (line 122) | function NavigationMenuLink({
  function NavigationMenuIndicator (line 138) | function NavigationMenuIndicator({

FILE: game-buying-guide/components/ui/pagination.tsx
  function Pagination (line 11) | function Pagination({ className, ...props }: React.ComponentProps<'nav'>) {
  function PaginationContent (line 23) | function PaginationContent({
  function PaginationItem (line 36) | function PaginationItem({ ...props }: React.ComponentProps<'li'>) {
  type PaginationLinkProps (line 40) | type PaginationLinkProps = {
  function PaginationLink (line 45) | function PaginationLink({
  function PaginationPrevious (line 68) | function PaginationPrevious({
  function PaginationNext (line 85) | function PaginationNext({
  function PaginationEllipsis (line 102) | function PaginationEllipsis({

FILE: game-buying-guide/components/ui/popover.tsx
  function Popover (line 8) | function Popover({
  function PopoverTrigger (line 14) | function PopoverTrigger({
  function PopoverContent (line 20) | function PopoverContent({
  function PopoverAnchor (line 42) | function PopoverAnchor({

FILE: game-buying-guide/components/ui/progress.tsx
  function Progress (line 8) | function Progress({

FILE: game-buying-guide/components/ui/radio-group.tsx
  function RadioGroup (line 9) | function RadioGroup({
  function RadioGroupItem (line 22) | function RadioGroupItem({

FILE: game-buying-guide/components/ui/resizable.tsx
  function ResizablePanelGroup (line 9) | function ResizablePanelGroup({
  function ResizablePanel (line 25) | function ResizablePanel({
  function ResizableHandle (line 31) | function ResizableHandle({

FILE: game-buying-guide/components/ui/scroll-area.tsx
  function ScrollArea (line 8) | function ScrollArea({
  function ScrollBar (line 31) | function ScrollBar({

FILE: game-buying-guide/components/ui/select.tsx
  function Select (line 9) | function Select({
  function SelectGroup (line 15) | function SelectGroup({
  function SelectValue (line 21) | function SelectValue({
  function SelectTrigger (line 27) | function SelectTrigger({
  function SelectContent (line 53) | function SelectContent({
  function SelectLabel (line 88) | function SelectLabel({
  function SelectItem (line 101) | function SelectItem({
  function SelectSeparator (line 125) | function SelectSeparator({
  function SelectScrollUpButton (line 138) | function SelectScrollUpButton({
  function SelectScrollDownButton (line 156) | function SelectScrollDownButton({

FILE: game-buying-guide/components/ui/separator.tsx
  function Separator (line 8) | function Separator({

FILE: game-buying-guide/components/ui/sheet.tsx
  function Sheet (line 9) | function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive....
  function SheetTrigger (line 13) | function SheetTrigger({
  function SheetClose (line 19) | function SheetClose({
  function SheetPortal (line 25) | function SheetPortal({
  function SheetOverlay (line 31) | function SheetOverlay({
  function SheetContent (line 47) | function SheetContent({
  function SheetHeader (line 84) | function SheetHeader({ className, ...props }: React.ComponentProps<'div'...
  function SheetFooter (line 94) | function SheetFooter({ className, ...props }: React.ComponentProps<'div'...
  function SheetTitle (line 104) | function SheetTitle({
  function SheetDescription (line 117) | function SheetDescription({

FILE: game-buying-guide/components/ui/sidebar.tsx
  constant SIDEBAR_COOKIE_NAME (line 28) | const SIDEBAR_COOKIE_NAME = 'sidebar_state'
  constant SIDEBAR_COOKIE_MAX_AGE (line 29) | const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7
  constant SIDEBAR_WIDTH (line 30) | const SIDEBAR_WIDTH = '16rem'
  constant SIDEBAR_WIDTH_MOBILE (line 31) | const SIDEBAR_WIDTH_MOBILE = '18rem'
  constant SIDEBAR_WIDTH_ICON (line 32) | const SIDEBAR_WIDTH_ICON = '3rem'
  constant SIDEBAR_KEYBOARD_SHORTCUT (line 33) | const SIDEBAR_KEYBOARD_SHORTCUT = 'b'
  type SidebarContextProps (line 35) | type SidebarContextProps = {
  function useSidebar (line 47) | function useSidebar() {
  function SidebarProvider (line 56) | function SidebarProvider({
  function Sidebar (line 154) | function Sidebar({
  function SidebarTrigger (line 256) | function SidebarTrigger({
  function SidebarRail (line 282) | function SidebarRail({ className, ...props }: React.ComponentProps<'butt...
  function SidebarInset (line 307) | function SidebarInset({ className, ...props }: React.ComponentProps<'mai...
  function SidebarInput (line 321) | function SidebarInput({
  function SidebarHeader (line 335) | function SidebarHeader({ className, ...props }: React.ComponentProps<'di...
  function SidebarFooter (line 346) | function SidebarFooter({ className, ...props }: React.ComponentProps<'di...
  function SidebarSeparator (line 357) | function SidebarSeparator({
  function SidebarContent (line 371) | function SidebarContent({ className, ...props }: React.ComponentProps<'d...
  function SidebarGroup (line 385) | function SidebarGroup({ className, ...props }: React.ComponentProps<'div...
  function SidebarGroupLabel (line 396) | function SidebarGroupLabel({
  function SidebarGroupAction (line 417) | function SidebarGroupAction({
  function SidebarGroupContent (line 440) | function SidebarGroupContent({
  function SidebarMenu (line 454) | function SidebarMenu({ className, ...props }: React.ComponentProps<'ul'>) {
  function SidebarMenuItem (line 465) | function SidebarMenuItem({ className, ...props }: React.ComponentProps<'...
  function SidebarMenuButton (line 498) | function SidebarMenuButton({
  function SidebarMenuAction (line 548) | function SidebarMenuAction({
  function SidebarMenuBadge (line 580) | function SidebarMenuBadge({
  function SidebarMenuSkeleton (line 602) | function SidebarMenuSkeleton({
  function SidebarMenuSub (line 640) | function SidebarMenuSub({ className, ...props }: React.ComponentProps<'u...
  function SidebarMenuSubItem (line 655) | function SidebarMenuSubItem({
  function SidebarMenuSubButton (line 669) | function SidebarMenuSubButton({

FILE: game-buying-guide/components/ui/skeleton.tsx
  function Skeleton (line 3) | function Skeleton({ className, ...props }: React.ComponentProps<'div'>) {

FILE: game-buying-guide/components/ui/slider.tsx
  function Slider (line 8) | function Slider({

FILE: game-buying-guide/components/ui/spinner.tsx
  function Spinner (line 5) | function Spinner({ className, ...props }: React.ComponentProps<'svg'>) {

FILE: game-buying-guide/components/ui/switch.tsx
  function Switch (line 8) | function Switch({

FILE: game-buying-guide/components/ui/table.tsx
  function Table (line 7) | function Table({ className, ...props }: React.ComponentProps<'table'>) {
  function TableHeader (line 22) | function TableHeader({ className, ...props }: React.ComponentProps<'thea...
  function TableBody (line 32) | function TableBody({ className, ...props }: React.ComponentProps<'tbody'...
  function TableFooter (line 42) | function TableFooter({ className, ...props }: React.ComponentProps<'tfoo...
  function TableRow (line 55) | function TableRow({ className, ...props }: React.ComponentProps<'tr'>) {
  function TableHead (line 68) | function TableHead({ className, ...props }: React.ComponentProps<'th'>) {
  function TableCell (line 81) | function TableCell({ className, ...props }: React.ComponentProps<'td'>) {
  function TableCaption (line 94) | function TableCaption({

FILE: game-buying-guide/components/ui/tabs.tsx
  function Tabs (line 8) | function Tabs({
  function TabsList (line 21) | function TabsList({
  function TabsTrigger (line 37) | function TabsTrigger({
  function TabsContent (line 53) | function TabsContent({

FILE: game-buying-guide/components/ui/textarea.tsx
  function Textarea (line 5) | function Textarea({ className, ...props }: React.ComponentProps<'textare...

FILE: game-buying-guide/components/ui/toast.tsx
  type ToastProps (line 115) | type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
  type ToastActionElement (line 117) | type ToastActionElement = React.ReactElement<typeof ToastAction>

FILE: game-buying-guide/components/ui/toaster.tsx
  function Toaster (line 13) | function Toaster() {

FILE: game-buying-guide/components/ui/toggle-group.tsx
  function ToggleGroup (line 17) | function ToggleGroup({
  function ToggleGroupItem (line 43) | function ToggleGroupItem({

FILE: game-buying-guide/components/ui/toggle.tsx
  function Toggle (line 31) | function Toggle({

FILE: game-buying-guide/components/ui/tooltip.tsx
  function TooltipProvider (line 8) | function TooltipProvider({
  function Tooltip (line 21) | function Tooltip({
  function TooltipTrigger (line 31) | function TooltipTrigger({
  function TooltipContent (line 37) | function TooltipContent({

FILE: game-buying-guide/components/ui/use-mobile.tsx
  constant MOBILE_BREAKPOINT (line 3) | const MOBILE_BREAKPOINT = 768
  function useIsMobile (line 5) | function useIsMobile() {

FILE: game-buying-guide/components/ui/use-toast.ts
  constant TOAST_LIMIT (line 8) | const TOAST_LIMIT = 1
  constant TOAST_REMOVE_DELAY (line 9) | const TOAST_REMOVE_DELAY = 1000000
  type ToasterToast (line 11) | type ToasterToast = ToastProps & {
  function genId (line 27) | function genId() {
  type ActionType (line 32) | type ActionType = typeof actionTypes
  type Action (line 34) | type Action =
  type State (line 52) | interface State {
  function dispatch (line 133) | function dispatch(action: Action) {
  type Toast (line 140) | type Toast = Omit<ToasterToast, 'id'>
  function toast (line 142) | function toast({ ...props }: Toast) {
  function useToast (line 171) | function useToast() {

FILE: game-buying-guide/hooks/use-game-search.ts
  function useGameSearch (line 6) | function useGameSearch() {

FILE: game-buying-guide/hooks/use-mobile.ts
  constant MOBILE_BREAKPOINT (line 3) | const MOBILE_BREAKPOINT = 768
  function useIsMobile (line 5) | function useIsMobile() {

FILE: game-buying-guide/hooks/use-toast.ts
  constant TOAST_LIMIT (line 8) | const TOAST_LIMIT = 1
  constant TOAST_REMOVE_DELAY (line 9) | const TOAST_REMOVE_DELAY = 1000000
  type ToasterToast (line 11) | type ToasterToast = ToastProps & {
  function genId (line 27) | function genId() {
  type ActionType (line 32) | type ActionType = typeof actionTypes
  type Action (line 34) | type Action =
  type State (line 52) | interface State {
  function dispatch (line 133) | function dispatch(action: Action) {
  type Toast (line 140) | type Toast = Omit<ToasterToast, 'id'>
  function toast (line 142) | function toast({ ...props }: Toast) {
  function useToast (line 171) | function useToast() {

FILE: game-buying-guide/lib/types.ts
  type Platform (line 1) | interface Platform {
  type AgentStatus (line 6) | interface AgentStatus {
  type PlatformAnalysis (line 16) | interface PlatformAnalysis {
  type GeminiPlatformResponse (line 32) | interface GeminiPlatformResponse {
  type SteamDBPriceHistory (line 36) | interface SteamDBPriceHistory {
  type SteamDBAgentStatus (line 47) | interface SteamDBAgentStatus {

FILE: game-buying-guide/lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {

FILE: lego-hunter/app/api/generate-urls/route.ts
  function POST (line 4) | async function POST(request: Request) {

FILE: lego-hunter/app/api/search-lego/route.ts
  type SearchLegoRequest (line 4) | interface SearchLegoRequest {
  function POST (line 10) | async function POST(request: Request) {
  function processRetailers (line 44) | async function processRetailers(
  function scrapeRetailer (line 114) | async function scrapeRetailer(

FILE: lego-hunter/app/layout.tsx
  function RootLayout (line 23) | function RootLayout({

FILE: lego-hunter/app/page.tsx
  function LegoFinderPage (line 15) | function LegoFinderPage() {
  function RetailerStatusCard (line 462) | function RetailerStatusCard({ retailer, logo, delay }: { retailer: Retai...
  function StatusIndicator (line 601) | function StatusIndicator({ status, stockFound, inStock }: { status: stri...
  function ResultsTable (line 628) | function ResultsTable({ results }: { results: ProductData[] }) {

FILE: lego-hunter/components/best-deal-card.tsx
  type BestDealCardProps (line 8) | interface BestDealCardProps {
  function BestDealCard (line 13) | function BestDealCard({ deal, results }: BestDealCardProps) {

FILE: lego-hunter/components/browser-preview.tsx
  type BrowserPreviewProps (line 5) | interface BrowserPreviewProps {
  function BrowserPreview (line 11) | function BrowserPreview({
  function LoadingBricks (line 97) | function LoadingBricks() {

FILE: lego-hunter/components/lego-confetti.tsx
  constant LEGO_COLORS (line 6) | const LEGO_COLORS = [
  function triggerLegoConfetti (line 18) | function triggerLegoConfetti() {
  function triggerConfettiAtElement (line 58) | function triggerConfettiAtElement(element: HTMLElement) {
  function triggerVictoryConfetti (line 77) | function triggerVictoryConfetti() {

FILE: lego-hunter/components/results-table.tsx
  type ResultsTableProps (line 7) | interface ResultsTableProps {
  type SortField (line 11) | type SortField = 'retailer' | 'price' | 'inStock'
  type SortDirection (line 12) | type SortDirection = 'asc' | 'desc'
  function ResultsTable (line 14) | function ResultsTable({ results }: ResultsTableProps) {

FILE: lego-hunter/components/retailer-card.tsx
  type RetailerCardProps (line 9) | interface RetailerCardProps {
  function RetailerCard (line 14) | function RetailerCard({ retailerStatus, logo }: RetailerCardProps) {
  function StatusIcon (line 141) | function StatusIcon({

FILE: lego-hunter/components/ui/button.tsx
  function Button (line 39) | function Button({

FILE: lego-hunter/components/ui/card.tsx
  function Card (line 5) | function Card({ className, ...props }: React.ComponentProps<"div">) {
  function CardHeader (line 18) | function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
  function CardTitle (line 31) | function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
  function CardDescription (line 41) | function CardDescription({ className, ...props }: React.ComponentProps<"...
  function CardAction (line 51) | function CardAction({ className, ...props }: React.ComponentProps<"div">) {
  function CardContent (line 64) | function CardContent({ className, ...props }: React.ComponentProps<"div"...
  function CardFooter (line 74) | function CardFooter({ className, ...props }: React.ComponentProps<"div">) {

FILE: lego-hunter/components/ui/input.tsx
  function Input (line 5) | function Input({ className, type, ...props }: React.ComponentProps<"inpu...

FILE: lego-hunter/lib/gemini-client.ts
  function generateRetailerUrls (line 37) | async function generateRetailerUrls(legoSetName: string): Promise<Retail...
  function analyzeBestDeal (line 78) | async function analyzeBestDeal(

FILE: lego-hunter/lib/retailers.ts
  type RetailerConfig (line 1) | interface RetailerConfig {
  constant DEFAULT_RETAILERS (line 8) | const DEFAULT_RETAILERS: RetailerConfig[] = [
  function buildSearchUrl (line 101) | function buildSearchUrl(retailer: RetailerConfig, searchTerm: string): s...

FILE: lego-hunter/lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {

FILE: lego-hunter/types/index.ts
  type Retailer (line 2) | interface Retailer {
  type ProductData (line 9) | interface ProductData {
  type RetailerStatus (line 19) | interface RetailerStatus {
  type DealAnalysis (line 30) | interface DealAnalysis {
  type SSEEventType (line 43) | type SSEEventType =
  type SSEEvent (line 52) | interface SSEEvent {
  type GenerateUrlsRequest (line 64) | interface GenerateUrlsRequest {
  type GenerateUrlsResponse (line 68) | interface GenerateUrlsResponse {
  type SearchLegoRequest (line 72) | interface SearchLegoRequest {
  type TinyFishRequest (line 79) | interface TinyFishRequest {
  type TinyFishSSEEvent (line 89) | interface TinyFishSSEEvent {

FILE: loan-decision-copilot/src/components/AgentCard.tsx
  type AgentCardProps (line 7) | interface AgentCardProps {
  function AgentCard (line 15) | function AgentCard({ bank, index, isSelected, onSelect, onExpandPreview ...

FILE: loan-decision-copilot/src/components/BankDetailPanel.tsx
  type BankDetailPanelProps (line 9) | interface BankDetailPanelProps {
  function BankDetailPanel (line 14) | function BankDetailPanel({ bank, onClose }: BankDetailPanelProps) {

FILE: loan-decision-copilot/src/components/LiveBrowserPreview.tsx
  type LiveBrowserPreviewProps (line 7) | interface LiveBrowserPreviewProps {
  function LiveBrowserPreview (line 13) | function LiveBrowserPreview({ streamingUrl, bankName, onClose }: LiveBro...

FILE: loan-decision-copilot/src/components/LoanTypeSelector.tsx
  type LoanTypeSelectorProps (line 49) | interface LoanTypeSelectorProps {
  function LoanTypeSelector (line 54) | function LoanTypeSelector({ selected, onSelect }: LoanTypeSelectorProps) {

FILE: loan-decision-copilot/src/components/LocationInput.tsx
  type LocationInputProps (line 7) | interface LocationInputProps {
  function LocationInput (line 13) | function LocationInput({ onSearch, isLoading, disabled }: LocationInputP...

FILE: loan-decision-copilot/src/components/NavLink.tsx
  type NavLinkCompatProps (line 5) | interface NavLinkCompatProps extends Omit<NavLinkProps, "className"> {

FILE: loan-decision-copilot/src/components/SearchProgress.tsx
  type SearchProgressProps (line 4) | interface SearchProgressProps {
  function SearchProgress (line 11) | function SearchProgress({

FILE: loan-decision-copilot/src/components/ui/button.tsx
  type ButtonProps (line 33) | interface ButtonProps

FILE: loan-decision-copilot/src/hooks/useLoanSearch.ts
  function useLoanSearch (line 6) | function useLoanSearch() {

FILE: loan-decision-copilot/src/integrations/supabase/client.ts
  constant SUPABASE_URL (line 5) | const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL;
  constant SUPABASE_PUBLISHABLE_KEY (line 6) | const SUPABASE_PUBLISHABLE_KEY = import.meta.env.VITE_SUPABASE_PUBLISHAB...

FILE: loan-decision-copilot/src/integrations/supabase/types.ts
  type Json (line 1) | type Json =
  type Database (line 9) | type Database = {
  type DatabaseWithoutInternals (line 34) | type DatabaseWithoutInternals = Omit<Database, "__InternalSupabase">
  type DefaultSchema (line 36) | type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, "p...
  type Tables (line 38) | type Tables<
  type TablesInsert (line 67) | type TablesInsert<
  type TablesUpdate (line 92) | type TablesUpdate<
  type Enums (line 117) | type Enums<
  type CompositeTypes (line 134) | type CompositeTypes<

FILE: loan-decision-copilot/src/lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {

FILE: loan-decision-copilot/src/types/loan.ts
  type LoanType (line 1) | type LoanType = 'personal' | 'home' | 'education' | 'business';
  type LoanTypeOption (line 3) | interface LoanTypeOption {
  type BankLoanInfo (line 10) | interface BankLoanInfo {
  type LoanAnalysisResult (line 20) | interface LoanAnalysisResult {
  type SearchState (line 33) | interface SearchState {

FILE: loan-decision-copilot/supabase/functions/analyze-loan/index.ts
  method start (line 72) | async start(controller) {

FILE: logistics-sentry/src/app/api/agent/run/route.js
  function POST (line 4) | async function POST(req) {

FILE: logistics-sentry/src/app/api/logistics/risk-assessment/route.js
  function normalizeRiskResult (line 3) | function normalizeRiskResult(result) {
  function POST (line 32) | async function POST(req) {

FILE: logistics-sentry/src/app/api/pricing/run/route.js
  function POST (line 5) | async function POST(req) {

FILE: logistics-sentry/src/app/competitive-pricing/page.js
  function PricingIntelligence (line 24) | function PricingIntelligence() {

FILE: logistics-sentry/src/app/layout.js
  function RootLayout (line 11) | function RootLayout({ children }) {

FILE: logistics-sentry/src/app/page.js
  function LogisticsDashboard (line 25) | function LogisticsDashboard() {

FILE: logistics-sentry/src/components/ActionPanel.js
  function ActionPanel (line 10) | function ActionPanel({ pendingActions, onProceed, onPause, onEscalate, o...

FILE: logistics-sentry/src/components/ActivityFeed.js
  function ActivityFeed (line 10) | function ActivityFeed({ activities }) {

FILE: logistics-sentry/src/components/AgentHeader.js
  function AgentHeader (line 6) | function AgentHeader({ status }) {

FILE: logistics-sentry/src/components/DecisionReasoning.js
  function DecisionReasoning (line 6) | function DecisionReasoning({ result }) {

FILE: logistics-sentry/src/components/InventoryAlert.js
  function InventoryAlert (line 12) | function InventoryAlert({

FILE: logistics-sentry/src/components/InventoryInput.js
  function InventoryInput (line 9) | function InventoryInput({ onInventoryLoaded }) {

FILE: logistics-sentry/src/components/LiveStream.js
  constant PHASES (line 8) | const PHASES = [
  function LiveStream (line 15) | function LiveStream({ events = [], isRunning, currentPhase }) {

FILE: logistics-sentry/src/components/MetricCard.js
  function MetricCard (line 4) | function MetricCard({ title, value, subtitle, icon: Icon, trend, variant...

FILE: logistics-sentry/src/components/RiskAssessment.js
  function RiskAssessment (line 4) | function RiskAssessment({ items }) {

FILE: logistics-sentry/src/components/TinyFishAgentAesthetics.js
  function TinyFishAgentAesthetics (line 6) | function TinyFishAgentAesthetics({ currentAction, targetUrl, isActive }) {

FILE: logistics-sentry/src/hooks/use-toast.js
  function toast (line 4) | function toast({ title, description }) {
  function useToast (line 11) | function useToast() {

FILE: logistics-sentry/src/lib/decision-engine.js
  function evaluateRisk (line 3) | function evaluateRisk(agentOutput) {

FILE: logistics-sentry/src/lib/logistics/agent.js
  constant SOURCE_KNOWLEDGE_BASE (line 6) | const SOURCE_KNOWLEDGE_BASE = {
  function buildDiscoverySources (line 61) | function buildDiscoverySources(origin_port, carrier) {
  function createSseParser (line 133) | function createSseParser(onEvent) {
  function analyzeSource (line 173) | async function analyzeSource(source) {
  function synthesizeRisk (line 259) | function synthesizeRisk(context, findings) {
  function assessDelayRisk (line 438) | async function assessDelayRisk(context) {

FILE: logistics-sentry/src/lib/pricing-intelligence.js
  constant TINYFISH_API_URL (line 3) | const TINYFISH_API_URL = "https://tinyfish.ai/v1/automation/run-sse";
  constant TINYFISH_API_KEY (line 4) | const TINYFISH_API_KEY = process.env.TINYFISH_API_KEY;
  function runPricingAnalysis (line 6) | async function runPricingAnalysis(competitorUrl, options = {}) {

FILE: logistics-sentry/src/lib/tinyfish.js
  constant TINYFISH_API_URL (line 3) | const TINYFISH_API_URL = "https://tinyfish.ai/v1/automation/run-sse";
  constant TINYFISH_API_KEY (line 4) | const TINYFISH_API_KEY = process.env.TINYFISH_API_KEY;
  function runAgent (line 6) | async function runAgent(sku, intendedUpdate, contextUrl, options = {}) {
  function runGenericAgent (line 80) | async function runGenericAgent(url, goal, options = {}) {

FILE: logistics-sentry/src/lib/utils.js
  function cn (line 4) | function cn(...inputs) {

FILE: openbox-deals/app/main.py
  class RateLimiter (line 33) | class RateLimiter:
    method __init__ (line 35) | def __init__(self, requests_per_minute: int = 5, max_ips: int = 10000):
    method _cleanup (line 41) | def _cleanup(self):
    method is_allowed (line 54) | def is_allowed(self, client_ip: str) -> bool:
    method time_until_allowed (line 78) | def time_until_allowed(self, client_ip: str) -> int:
  class ActiveSearchTracker (line 85) | class ActiveSearchTracker:
    method __init__ (line 87) | def __init__(self, max_active: int = 1000, search_timeout: int = 300):
    method _cleanup (line 93) | def _cleanup(self):
    method start_search (line 105) | def start_search(self, client_ip: str, query: str) -> str:
    method is_searching (line 111) | def is_searching(self, client_ip: str) -> bool:
    method end_search (line 122) | def end_search(self, client_ip: str):
  function lifespan (line 136) | async def lifespan(app: FastAPI):
  function create_fresh_session (line 144) | async def create_fresh_session() -> aiohttp.ClientSession:
  function get_healthy_session (line 158) | async def get_healthy_session() -> aiohttp.ClientSession:
  function validate_query (line 195) | def validate_query(q: str) -> str:
  function validate_url (line 211) | def validate_url(url: str) -> Optional[str]:
  function sanitize_product (line 229) | def sanitize_product(product: dict) -> dict:
  function root (line 298) | def root():
  function list_sites (line 306) | def list_sites():
  function search_status (line 316) | def search_status(request: Request):
  function search_live (line 345) | async def search_live(
  function extract_products (line 594) | def extract_products(result_data) -> list:
  function filter_by_price (line 664) | def filter_by_price(products: list, max_price: float, strict: bool = Fal...

FILE: research-sentry/app/api/citations/track/route.ts
  function POST (line 4) | async function POST(req: NextRequest) {

FILE: research-sentry/app/api/compare/route.ts
  function POST (line 4) | async function POST(req: NextRequest) {

FILE: research-sentry/app/api/conversation/route.ts
  function POST (line 6) | async function POST(req: NextRequest) {

FILE: research-sentry/app/api/emails/extract/route.ts
  type AuthorInfo (line 9) | interface AuthorInfo {
  function capitalizeNamePart (line 15) | function capitalizeNamePart(part: string): string {
  function deriveNameFromEmail (line 21) | function deriveNameFromEmail(email: string): Pick<AuthorInfo, 'firstName...
  function tryParseJsonString (line 56) | function tryParseJsonString(s: string): any | null {
  function normalizeArxivPdfUrl (line 65) | function normalizeArxivPdfUrl(url: string): string {
  function findAuthorsArray (line 83) | function findAuthorsArray(obj: any): AuthorInfo[] {
  function findEmailsArray (line 139) | function findEmailsArray(obj: any): string[] {
  function POST (line 185) | async function POST(req: NextRequest) {

FILE: research-sentry/app/api/export/bibtex/route.ts
  function POST (line 3) | async function POST(req: NextRequest) {

FILE: research-sentry/app/api/health/route.ts
  function GET (line 3) | async function GET() {

FILE: research-sentry/app/api/search/text/route.ts
  function POST (line 7) | async function POST(req: NextRequest) {

FILE: research-sentry/app/api/search/voice/route.ts
  function POST (line 8) | async function POST(req: NextRequest) {

FILE: research-sentry/app/api/summarize/route.ts
  function POST (line 6) | async function POST(req: NextRequest) {

FILE: research-sentry/app/layout.tsx
  function RootLayout (line 5) | function RootLayout({ children }: { children: React.ReactNode }) {

FILE: research-sentry/app/page.tsx
  function Home (line 16) | function Home() {

FILE: research-sentry/components/AudioPlayer.tsx
  type AudioPlayerProps (line 6) | interface AudioPlayerProps {
  function AudioPlayer (line 13) | function AudioPlayer({ src, title, onGenerate, isGenerating }: AudioPlay...

FILE: research-sentry/components/CitationTracker.tsx
  type CitationTrackerProps (line 8) | interface CitationTrackerProps {
  function CitationTracker (line 13) | function CitationTracker({ paper, onClose }: CitationTrackerProps) {

FILE: research-sentry/components/CoPilotMode.tsx
  type CoPilotModeProps (line 8) | interface CoPilotModeProps {
  function CoPilotMode (line 13) | function CoPilotMode({ papers, onExit }: CoPilotModeProps) {

FILE: research-sentry/components/ConversationInterface.tsx
  type ConversationInterfaceProps (line 8) | interface ConversationInterfaceProps {
  function ConversationInterface (line 12) | function ConversationInterface({ initialContext }: ConversationInterface...

FILE: research-sentry/components/ErrorMessage.tsx
  type ErrorMessageProps (line 5) | interface ErrorMessageProps {
  function ErrorMessage (line 10) | function ErrorMessage({ message, onRetry }: ErrorMessageProps) {

FILE: research-sentry/components/LoadingSpinner.tsx
  type LoadingSpinnerProps (line 3) | interface LoadingSpinnerProps {
  function LoadingSpinner (line 7) | function LoadingSpinner({ size = 'md' }: LoadingSpinnerProps) {

FILE: research-sentry/components/PaperCard.tsx
  type AuthorInfo (line 8) | interface AuthorInfo {
  type PaperCardProps (line 14) | interface PaperCardProps {
  function PaperCard (line 21) | function PaperCard({ paper, onSelect, selected, onTrack }: PaperCardProp...

FILE: research-sentry/components/PaperComparison.tsx
  type PaperComparisonProps (line 8) | interface PaperComparisonProps {
  function PaperComparison (line 13) | function PaperComparison({ papers, onClose }: PaperComparisonProps) {

FILE: research-sentry/components/PaperSummary.tsx
  type SummaryLength (line 7) | type SummaryLength = 'short' | 'medium' | 'long';
  type PaperSummaryProps (line 9) | interface PaperSummaryProps {
  function PaperSummary (line 15) | function PaperSummary({ paper, length = 'medium', title = 'AI Summary' }...

FILE: research-sentry/components/ResultsGrid.tsx
  type ResultsGridProps (line 9) | interface ResultsGridProps {
  function ResultsGrid (line 17) | function ResultsGrid({ results, selectedPapers, onToggleSelect, onExport...

FILE: research-sentry/components/SearchInterface.tsx
  type SearchInterfaceProps (line 8) | interface SearchInterfaceProps {
  constant SOURCES (line 14) | const SOURCES: { value: SourceType; label: string }[] = [
  function SearchInterface (line 25) | function SearchInterface({ onTextSearch, onVoiceSearch, loading }: Searc...

FILE: research-sentry/components/TinyFishAgentTerminal.tsx
  type AgentLog (line 6) | interface AgentLog {
  type TinyFishAgentTerminalProps (line 13) | interface TinyFishAgentTerminalProps {
  function TinyFishAgentTerminal (line 18) | function TinyFishAgentTerminal({ topic = "research", sources = ["arxiv"]...

FILE: research-sentry/components/VoiceRecorder.tsx
  type VoiceRecorderProps (line 7) | interface VoiceRecorderProps {
  function VoiceRecorder (line 12) | function VoiceRecorder({ onRecordingComplete, disabled }: VoiceRecorderP...

FILE: research-sentry/components/WorkflowSelector.tsx
  function WorkflowSelector (line 7) | function WorkflowSelector() {

FILE: research-sentry/hooks/useVoiceCommands.ts
  type SpeechRecognitionEvent (line 6) | interface SpeechRecognitionEvent extends Event {
  type SpeechRecognitionResultList (line 11) | interface SpeechRecognitionResultList {
  type SpeechRecognitionResult (line 17) | interface SpeechRecognitionResult {
  type SpeechRecognitionAlternative (line 24) | interface SpeechRecognitionAlternative {
  type SpeechRecognitionErrorEvent (line 29) | interface SpeechRecognitionErrorEvent extends Event {
  type SpeechRecognition (line 34) | interface SpeechRecognition extends EventTarget {
  type Window (line 47) | interface Window {
  type VoiceCommand (line 53) | interface VoiceCommand {
  function useVoiceCommands (line 59) | function useVoiceCommands(commands: VoiceCommand[]) {

FILE: research-sentry/lib/aggregator.ts
  function aggregateAndDeduplicate (line 3) | function aggregateAndDeduplicate(results: ResearchPaper[][]): ResearchPa...

FILE: research-sentry/lib/audio-utils.ts
  type AudioRecorderConfig (line 1) | interface AudioRecorderConfig {
  class AudioRecorder (line 6) | class AudioRecorder {
    method startRecording (line 11) | async startRecording(onDataAvailable?: (data: Blob) => void): Promise<...
    method stopRecording (line 36) | stopRecording(): Promise<Blob> {
    method cancelRecording (line 54) | cancelRecording(): void {
    method cleanup (line 61) | private cleanup(): void {
    method getSupportedMimeType (line 70) | private getSupportedMimeType(): string {
    method isRecording (line 87) | isRecording(): boolean {
  function formatDuration (line 92) | function formatDuration(seconds: number): string {
  function checkMicrophoneSupport (line 98) | function checkMicrophoneSupport(): boolean {

FILE: research-sentry/lib/citation-tracker.ts
  type TrackedPaper (line 6) | interface TrackedPaper {
  function analyzeCitationTrend (line 28) | async function analyzeCitationTrend(paper: ResearchPaper): Promise<Track...

FILE: research-sentry/lib/comparator.ts
  type ComparisonPoint (line 6) | interface ComparisonPoint {
  type ComparisonResult (line 12) | interface ComparisonResult {
  function comparePapers (line 17) | async function comparePapers(papers: ResearchPaper[]): Promise<Compariso...

FILE: research-sentry/lib/conversation.ts
  function generateConversationResponse (line 6) | async function generateConversationResponse(

FILE: research-sentry/lib/email-utils.ts
  function extractEmailsFromText (line 1) | function extractEmailsFromText(input: string): string[] {

FILE: research-sentry/lib/intent-parser.ts
  function parseSearchIntent (line 6) | async function parseSearchIntent(query: string): Promise<SearchCriteria> {

FILE: research-sentry/lib/mino.ts
  function runMinoAutomation (line 4) | async function runMinoAutomation(

FILE: research-sentry/lib/pdf-utils.ts
  function fetchPdfText (line 1) | async function fetchPdfText(
  function safeParseUrl (line 86) | function safeParseUrl(value: string): URL | null {
  function isSafeHttpUrl (line 94) | function isSafeHttpUrl(url: URL): boolean {
  function isPrivateIp (line 102) | function isPrivateIp(host: string): boolean {
  function isIpv4 (line 121) | function isIpv4(host: string): boolean {
  function isIpv6 (line 125) | function isIpv6(host: string): boolean {

FILE: research-sentry/lib/search.ts
  function parseMinoResponse (line 18) | function parseMinoResponse(rawResponse: any): any[] {
  function findPapersArray (line 46) | function findPapersArray(obj: any): any[] {
  function fallbackArxiv (line 80) | async function fallbackArxiv(topic: string): Promise<ResearchPaper[]> {
  function fallbackSemanticScholar (line 101) | async function fallbackSemanticScholar(topic: string): Promise<ResearchP...
  function scrapeWithMino (line 121) | async function scrapeWithMino(
  function scrapeArxiv (line 202) | async function scrapeArxiv(criteria: SearchCriteria, timeoutMs?: number)...
  function scrapePubmed (line 210) | async function scrapePubmed(criteria: SearchCriteria, timeoutMs?: number...
  function scrapeSemanticScholar (line 217) | async function scrapeSemanticScholar(criteria: SearchCriteria, timeoutMs...
  function scrapeGoogleScholar (line 224) | async function scrapeGoogleScholar(criteria: SearchCriteria, timeoutMs?:...
  function scrapeIEEE (line 230) | async function scrapeIEEE(criteria: SearchCriteria, timeoutMs?: number):...
  function scrapeSSRN (line 237) | async function scrapeSSRN(criteria: SearchCriteria, timeoutMs?: number):...
  function scrapeCORE (line 242) | async function scrapeCORE(criteria: SearchCriteria, timeoutMs?: number):...
  function scrapeDOAJ (line 247) | async function scrapeDOAJ(criteria: SearchCriteria, timeoutMs?: number):...
  function scrapeSource (line 252) | async function scrapeSource(source: string, criteria: SearchCriteria, ti...
  function searchResearchPapers (line 267) | async function searchResearchPapers(criteria: SearchCriteria): Promise<S...

FILE: research-sentry/lib/summarizer.ts
  function generatePaperSummary (line 6) | async function generatePaperSummary(paper: ResearchPaper, length: 'short...
  function synthesizeSpeech (line 32) | async function synthesizeSpeech(text: string) {

FILE: research-sentry/lib/types.ts
  type SearchCriteria (line 1) | interface SearchCriteria {
  type SourceType (line 10) | type SourceType = 'arxiv' | 'pubmed' | 'semantic_scholar' | 'google_scho...
  type ResearchPaper (line 12) | interface ResearchPaper {
  type SearchResult (line 25) | interface SearchResult {
  type Message (line 32) | interface Message {
  type ConversationState (line 40) | interface ConversationState {

FILE: research-sentry/lib/whisper.ts
  function transcribeAudio (line 5) | async function transcribeAudio(buffer: Buffer, filename = 'audio.webm') {

FILE: research-sentry/lib/workflows.ts
  type WorkflowStep (line 3) | interface WorkflowStep {
  type ResearchWorkflow (line 13) | interface ResearchWorkflow {
  constant WORKFLOWS (line 20) | const WORKFLOWS: ResearchWorkflow[] = [

FILE: restaurant-comparison-tool/src/App.tsx
  function AppContent (line 9) | function AppContent() {
  function App (line 50) | function App() {

FILE: restaurant-comparison-tool/src/components/layout/Footer.tsx
  function Footer (line 3) | function Footer() {

FILE: restaurant-comparison-tool/src/components/layout/Header.tsx
  function Header (line 4) | function Header() {

FILE: restaurant-comparison-tool/src/components/live/AgentStatusIndicator.tsx
  type AgentStatusIndicatorProps (line 5) | interface AgentStatusIndicatorProps {
  constant STATUS_CONFIG (line 10) | const STATUS_CONFIG: Record<AgentStatus, { icon: typeof Search; label: s...
  function AgentStatusIndicator (line 21) | function AgentStatusIndicator({ status, currentStep }: AgentStatusIndica...

FILE: restaurant-comparison-tool/src/components/live/LiveBrowserPreview.tsx
  type LiveBrowserPreviewProps (line 7) | interface LiveBrowserPreviewProps {
  function LiveBrowserPreview (line 13) | function LiveBrowserPreview({ streamingUrl, restaurantName, onClose }: L...
  type MiniPreviewProps (line 86) | interface MiniPreviewProps {
  function MiniPreview (line 91) | function MiniPreview({ streamingUrl, onClick }: MiniPreviewProps) {

FILE: restaurant-comparison-tool/src/components/live/LiveSearchPanel.tsx
  type LiveSearchPanelProps (line 10) | interface LiveSearchPanelProps {
  function LiveSearchPanel (line 16) | function LiveSearchPanel({ agents, city, onCancel }: LiveSearchPanelProp...

FILE: restaurant-comparison-tool/src/components/live/RestaurantAgentCard.tsx
  type RestaurantAgentCardProps (line 10) | interface RestaurantAgentCardProps {
  function RestaurantAgentCard (line 15) | function RestaurantAgentCard({ agent, onExpandPreview }: RestaurantAgent...
  function ElapsedTime (line 82) | function ElapsedTime({ startedAt }: { startedAt: number }) {

FILE: restaurant-comparison-tool/src/components/results/AgentLoadingCard.tsx
  type AgentLoadingCardProps (line 8) | interface AgentLoadingCardProps {
  constant STATUS_CONFIG (line 13) | const STATUS_CONFIG: Record<AgentStatus, { icon: typeof Search; label: s...
  function AgentLoadingCard (line 24) | function AgentLoadingCard({ agent, onExpandPreview }: AgentLoadingCardPr...
  function ElapsedTime (line 102) | function ElapsedTime({ startedAt }: { startedAt: number }) {

FILE: restaurant-comparison-tool/src/components/results/AllergenRiskBadge.tsx
  type AllergenRiskBadgeProps (line 7) | interface AllergenRiskBadgeProps {
  constant RISK_STYLES (line 11) | const RISK_STYLES: Record<string, string> = {
  function AllergenRiskBadge (line 18) | function AllergenRiskBadge({ risk }: AllergenRiskBadgeProps) {

FILE: restaurant-comparison-tool/src/components/results/AllergenRiskPanel.tsx
  type AllergenRiskPanelProps (line 5) | interface AllergenRiskPanelProps {
  function AllergenRiskPanel (line 9) | function AllergenRiskPanel({ risks }: AllergenRiskPanelProps) {

FILE: restaurant-comparison-tool/src/components/results/ComparisonDashboard.tsx
  type ComparisonDashboardProps (line 13) | interface ComparisonDashboardProps {
  function ComparisonDashboard (line 22) | function ComparisonDashboard({

FILE: restaurant-comparison-tool/src/components/results/ConfidenceIndicator.tsx
  type ConfidenceIndicatorProps (line 5) | interface ConfidenceIndicatorProps {
  constant CONFIG (line 9) | const CONFIG: Record<ConfidenceLevel, { icon: typeof ShieldCheck; label:...
  function ConfidenceIndicator (line 27) | function ConfidenceIndicator({ level }: ConfidenceIndicatorProps) {

FILE: restaurant-comparison-tool/src/components/results/FitExplanation.tsx
  type FitExplanationProps (line 3) | interface FitExplanationProps {
  function FitExplanation (line 9) | function FitExplanation({ explanation, pros, cons }: FitExplanationProps) {

FILE: restaurant-comparison-tool/src/components/results/GoogleMapsLink.tsx
  type GoogleMapsLinkProps (line 4) | interface GoogleMapsLinkProps {
  function GoogleMapsLink (line 8) | function GoogleMapsLink({ url }: GoogleMapsLinkProps) {

FILE: restaurant-comparison-tool/src/components/results/RestaurantResultCard.tsx
  type RestaurantResultCardProps (line 10) | interface RestaurantResultCardProps {
  function getScoreBg (line 18) | function getScoreBg(score: number) {
  function getFitLabel (line 24) | function getFitLabel(score: number) {
  constant CONFIDENCE_ICON (line 31) | const CONFIDENCE_ICON: Record<ConfidenceLevel, typeof ShieldCheck> = {
  constant RISK_DOT (line 37) | const RISK_DOT: Record<RiskLevel, string> = {
  function RestaurantResultCard (line 44) | function RestaurantResultCard({ result, searchParams, rank, index, onCli...

FILE: restaurant-comparison-tool/src/components/results/ResultDetailPanel.tsx
  type ResultDetailPanelProps (line 11) | interface ResultDetailPanelProps {
  function ResultDetailPanel (line 17) | function ResultDetailPanel({ result, searchParams, onClose }: ResultDeta...

FILE: restaurant-comparison-tool/src/components/results/SafetyScoreRing.tsx
  type SafetyScoreRingProps (line 4) | interface SafetyScoreRingProps {
  constant SIZES (line 9) | const SIZES = {
  function getScoreRingColor (line 15) | function getScoreRingColor(score: number): string {
  function SafetyScoreRing (line 21) | function SafetyScoreRing({ score, size = 'md' }: SafetyScoreRingProps) {

FILE: restaurant-comparison-tool/src/components/search/AllergenSelector.tsx
  type AllergenSelectorProps (line 5) | interface AllergenSelectorProps {
  function AllergenSelector (line 11) | function AllergenSelector({ selected, onChange, disabled }: AllergenSele...

FILE: restaurant-comparison-tool/src/components/search/PreferenceSelector.tsx
  type PreferenceSelectorProps (line 5) | interface PreferenceSelectorProps {
  function PreferenceSelector (line 11) | function PreferenceSelector({ selected, onChange, disabled }: Preference...

FILE: restaurant-comparison-tool/src/components/search/RestaurantInput.tsx
  type RestaurantInputProps (line 6) | interface RestaurantInputProps {
  function RestaurantInput (line 12) | function RestaurantInput({ restaurants, onChange, disabled }: Restaurant...

FILE: restaurant-comparison-tool/src/components/search/SearchForm.tsx
  type SearchFormProps (line 13) | interface SearchFormProps {
  function SearchForm (line 18) | function SearchForm({ onSearch, isSearching }: SearchFormProps) {

FILE: restaurant-comparison-tool/src/components/ui/badge.tsx
  function Badge (line 29) | function Badge({

FILE: restaurant-comparison-tool/src/components/ui/button.tsx
  function Button (line 41) | function Button({

FILE: restaurant-comparison-tool/src/components/ui/card.tsx
  function Card (line 5) | function Card({ className, ...props }: React.ComponentProps<"div">) {
  function CardHeader (line 18) | function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
  function CardTitle (line 31) | function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
  function CardDescription (line 41) | function CardDescription({ className, ...props }: React.ComponentProps<"...
  function CardAction (line 51) | function CardAction({ className, ...props }: React.ComponentProps<"div">) {
  function CardContent (line 64) | function CardContent({ className, ...props }: React.ComponentProps<"div"...
  function CardFooter (line 74) | function CardFooter({ className, ...props }: React.ComponentProps<"div">) {

FILE: restaurant-comparison-tool/src/components/ui/dialog.tsx
  function Dialog (line 8) | function Dialog({
  function DialogTrigger (line 14) | function DialogTrigger({
  function DialogPortal (line 20) | function DialogPortal({
  function DialogClose (line 26) | function DialogClose({
  function DialogOverlay (line 32) | function DialogOverlay({
  function DialogContent (line 48) | function DialogContent({
  function DialogHeader (line 82) | function DialogHeader({ className, ...props }: React.ComponentProps<"div...
  function DialogFooter (line 92) | function DialogFooter({
  function DialogTitle (line 119) | function DialogTitle({
  function DialogDescription (line 132) | function DialogDescription({

FILE: restaurant-comparison-tool/src/components/ui/input.tsx
  function Input (line 5) | function Input({ className, type, ...props }: React.ComponentProps<"inpu...

FILE: restaurant-comparison-tool/src/components/ui/scroll-area.tsx
  function ScrollArea (line 8) | function ScrollArea({
  function ScrollBar (line 31) | function ScrollBar({

FILE: restaurant-comparison-tool/src/components/ui/select.tsx
  function Select (line 7) | function Select({
  function SelectGroup (line 13) | function SelectGroup({
  function SelectValue (line 19) | function SelectValue({
  function SelectTrigger (line 25) | function SelectTrigger({
  function SelectContent (line 51) | function SelectContent({
  function SelectLabel (line 88) | function SelectLabel({
  function SelectItem (line 101) | function SelectItem({
  function SelectSeparator (line 128) | function SelectSeparator({
  function SelectScrollUpButton (line 141) | function SelectScrollUpButton({
  function SelectScrollDownButton (line 159) | function SelectScrollDownButton({

FILE: restaurant-comparison-tool/src/components/ui/separator.tsx
  function Separator (line 6) | function Separator({

FILE: restaurant-comparison-tool/src/components/ui/skeleton.tsx
  function Skeleton (line 3) | function Skeleton({ className, ...props }: React.ComponentProps<"div">) {

FILE: restaurant-comparison-tool/src/components/ui/toggle.tsx
  function Toggle (line 31) | function Toggle({

FILE: restaurant-comparison-tool/src/components/ui/tooltip.tsx
  function TooltipProvider (line 8) | function TooltipProvider({
  function Tooltip (line 21) | function Tooltip({
  function TooltipTrigger (line 27) | function TooltipTrigger({
  function TooltipContent (line 33) | function TooltipContent({

FILE: restaurant-comparison-tool/src/context/SearchContext.tsx
  function inferStatus (line 13) | function inferStatus(step: string): AgentStatus {
  function updateAgent (line 21) | function updateAgent(state: AppState, id: string, updates: Partial<AppSt...
  function searchReducer (line 33) | function searchReducer(state: AppState, action: AppAction): AppState {
  function SearchProvider (line 128) | function SearchProvider({ children }: { children: ReactNode }) {
  function useSearchContext (line 137) | function useSearchContext() {

FILE: restaurant-comparison-tool/src/hooks/useRestaurantSearch.ts
  function useRestaurantSearch (line 7) | function useRestaurantSearch() {

FILE: restaurant-comparison-tool/src/lib/allergens.ts
  constant ALLERGEN_INFO (line 3) | const ALLERGEN_INFO: Record<Allergen, { label: string; description: stri...
  constant ALL_ALLERGENS (line 20) | const ALL_ALLERGENS = Object.keys(ALLERGEN_INFO) as Allergen[];
  constant PREFERENCE_INFO (line 22) | const PREFERENCE_INFO: Record<DietaryPreference, { label: string; descri...
  constant ALL_PREFERENCES (line 33) | const ALL_PREFERENCES = Object.keys(PREFERENCE_INFO) as DietaryPreferenc...

FILE: restaurant-comparison-tool/src/lib/constants.ts
  constant TINYFISH_API_URL (line 1) | const TINYFISH_API_URL = 'https://agent.tinyfish.ai/v1/automation/run-sse';
  constant MIN_RESTAURANTS (line 3) | const MIN_RESTAURANTS = 2;
  constant MAX_RESTAURANTS (line 4) | const MAX_RESTAURANTS = 5;
  constant AGENT_TIMEOUT_MS (line 6) | const AGENT_TIMEOUT_MS = 120_000;
  constant APP_NAME (line 8) | const APP_NAME = 'SafeDine';
  constant APP_TAGLINE (line 9) | const APP_TAGLINE = 'Restaurant Safety Intelligence';

FILE: restaurant-comparison-tool/src/lib/goal-builder.ts
  constant ALLERGEN_LABELS (line 3) | const ALLERGEN_LABELS: Record<Allergen, string> = {
  function buildAgentGoal (line 20) | function buildAgentGoal(

FILE: restaurant-comparison-tool/src/lib/score-calculator.ts
  function calculateAdjustedScore (line 3) | function calculateAdjustedScore(
  function getScoreColor (line 30) | function getScoreColor(score: number): string {
  function getScoreLabel (line 36) | function getScoreLabel(score: number): string {

FILE: restaurant-comparison-tool/src/lib/tinyfish-client.ts
  type TinyFishRequestConfig (line 4) | interface TinyFishRequestConfig {
  type SSECallbacks (line 9) | type SSECallbacks = {
  function parseSSELine (line 16) | function parseSSELine(line: string): TinyFishSSEEvent | null {
  function startTinyFishAgent (line 25) | function startTinyFishAgent(

FILE: restaurant-comparison-tool/src/lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {

FILE: restaurant-comparison-tool/src/types/index.ts
  type Allergen (line 1) | type Allergen =
  type DietaryPreference (line 17) | type DietaryPreference =
  type SearchParams (line 27) | interface SearchParams {
  type AgentStatus (line 34) | type AgentStatus =
  type AgentStep (line 44) | interface AgentStep {
  type RestaurantAgentState (line 49) | interface RestaurantAgentState {
  type RiskLevel (line 62) | type RiskLevel = 'low' | 'moderate' | 'high' | 'critical';
  type ConfidenceLevel (line 63) | type ConfidenceLevel = 'high' | 'medium' | 'low';
  type AllergenRisk (line 65) | interface AllergenRisk {
  type SafetySignal (line 73) | interface SafetySignal {
  type RestaurantSafetyData (line 80) | interface RestaurantSafetyData {
  type TinyFishSSEEvent (line 106) | interface TinyFishSSEEvent {
  type AppPhase (line 118) | type AppPhase = 'input' | 'searching' | 'results';
  type AppState (line 120) | interface AppState {
  type AppAction (line 128) | type AppAction =

FILE: scholarship-finder/src/components/CompareButton.tsx
  type CompareButtonProps (line 5) | interface CompareButtonProps {
  function CompareButton (line 10) | function CompareButton({ selectedCount, onCompare }: CompareButtonProps) {

FILE: scholarship-finder/src/components/CompareDashboard.tsx
  type CompareDashboardProps (line 8) | interface CompareDashboardProps {
  function CompareDashboard (line 13) | function CompareDashboard({ scholarships, onClose }: CompareDashboardPro...

FILE: scholarship-finder/src/components/Header.tsx
  function Header (line 3) | function Header() {

FILE: scholarship-finder/src/components/LoadingAnimation.tsx
  type ScholarshipUrl (line 8) | interface ScholarshipUrl {
  type AgentStatus (line 14) | interface AgentStatus {
  type SearchState (line 26) | interface SearchState {
  type LoadingAnimationProps (line 34) | interface LoadingAnimationProps {
  function LoadingAnimation (line 38) | function LoadingAnimation({ searchState }: LoadingAnimationProps) {
  function AgentCard (line 186) | function AgentCard({

FILE: scholarship-finder/src/components/NavLink.tsx
  type NavLinkCompatProps (line 5) | interface NavLinkCompatProps extends Omit<NavLinkProps, "className"> {

FILE: scholarship-finder/src/components/ScholarshipCard.tsx
  type ScholarshipCardProps (line 8) | interface ScholarshipCardProps {
  function ScholarshipCard (line 13) | function ScholarshipCard({ scholarship, index }: ScholarshipCardProps) {

FILE: scholarship-finder/src/components/SearchForm.tsx
  type SearchFormProps (line 14) | interface SearchFormProps {
  function SearchForm (line 47) | function SearchForm({ onSearch, isLoading }: SearchFormProps) {

FILE: scholarship-finder/src/components/SearchResults.tsx
  type SearchResultsProps (line 8) | interface SearchResultsProps {
  function SearchResults (line 18) | function SearchResults({ scholarships, searchSummary, searchParams }: Se...

FILE: scholarship-finder/src/components/SelectableScholarshipCard.tsx
  type SelectableScholarshipCardProps (line 10) | interface SelectableScholarshipCardProps {
  function SelectableScholarshipCard (line 17) | function SelectableScholarshipCard({ scholarship, index, isSelected, onT...

FILE: scholarship-finder/src/components/ui/badge.tsx
  type BadgeProps (line 23) | interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, Varia...
  function Badge (line 25) | function Badge({ className, variant, ...props }: BadgeProps) {

FILE: scholarship-finder/src/components/ui/button.tsx
  type ButtonProps (line 33) | interface ButtonProps

FILE: scholarship-finder/src/components/ui/calendar.tsx
  type CalendarProps (line 8) | type CalendarProps = React.ComponentProps<typeof DayPicker>;
  function Calendar (line 10) | function Calendar({ className, classNames, showOutsideDays = true, ...pr...

FILE: scholarship-finder/src/components/ui/carousel.tsx
  type CarouselApi (line 8) | type CarouselApi = UseEmblaCarouselType[1];
  type UseCarouselParameters (line 9) | type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
  type CarouselOptions (line 10) | type CarouselOptions = UseCarouselParameters[0];
  type CarouselPlugin (line 11) | type CarouselPlugin = UseCarouselParameters[1];
  type CarouselProps (line 13) | type CarouselProps = {
  type CarouselContextProps (line 20) | type CarouselContextProps = {
  function useCarousel (line 31) | function useCarousel() {

FILE: scholarship-finder/src/components/ui/chart.tsx
  constant THEMES (line 7) | const THEMES = { light: "", dark: ".dark" } as const;
  type ChartConfig (line 9) | type ChartConfig = {
  type ChartContextProps (line 16) | type ChartContextProps = {
  function useChart (line 22) | function useChart() {
  function getPayloadConfigFromPayload (line 278) | function getPayloadConfigFromPayload(config: ChartConfig, payload: unkno...

FILE: scholarship-finder/src/components/ui/command.tsx
  type CommandDialogProps (line 24) | interface CommandDialogProps extends DialogProps {}

FILE: scholarship-finder/src/components/ui/form.tsx
  type FormFieldContextValue (line 11) | type FormFieldContextValue<
  type FormItemContextValue (line 56) | type FormItemContextValue = {

FILE: scholarship-finder/src/components/ui/pagination.tsx
  type PaginationLinkProps (line 29) | type PaginationLinkProps = {

FILE: scholarship-finder/src/components/ui/sheet.tsx
  type SheetContentProps (line 50) | interface SheetContentProps

FILE: scholarship-finder/src/components/ui/sidebar.tsx
  constant SIDEBAR_COOKIE_NAME (line 15) | const SIDEBAR_COOKIE_NAME = "sidebar:state";
  constant SIDEBAR_COOKIE_MAX_AGE (line 16) | const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
  constant SIDEBAR_WIDTH (line 17) | const SIDEBAR_WIDTH = "16rem";
  constant SIDEBAR_WIDTH_MOBILE (line 18) | const SIDEBAR_WIDTH_MOBILE = "18rem";
  constant SIDEBAR_WIDTH_ICON (line 19) | const SIDEBAR_WIDTH_ICON = "3rem";
  constant SIDEBAR_KEYBOARD_SHORTCUT (line 20) | const SIDEBAR_KEYBOARD_SHORTCUT = "b";
  type SidebarContext (line 22) | type SidebarContext = {
  function useSidebar (line 34) | function useSidebar() {

FILE: scholarship-finder/src/components/ui/skeleton.tsx
  function Skeleton (line 3) | function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivE...

FILE: scholarship-finder/src/components/ui/sonner.tsx
  type ToasterProps (line 4) | type ToasterProps = React.ComponentProps<typeof Sonner>;

FILE: scholarship-finder/src/components/ui/textarea.tsx
  type TextareaProps (line 5) | interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAre...

FILE: scholarship-finder/src/components/ui/toast.tsx
  type ToastProps (line 97) | type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
  type ToastActionElement (line 99) | type ToastActionElement = React.ReactElement<typeof ToastAction>;

FILE: scholarship-finder/src/components/ui/toaster.tsx
  function Toaster (line 4) | function Toaster() {

FILE: scholarship-finder/src/hooks/use-mobile.tsx
  constant MOBILE_BREAKPOINT (line 3) | const MOBILE_BREAKPOINT = 768;
  function useIsMobile (line 5) | function useIsMobile() {

FILE: scholarship-finder/src/hooks/use-toast.ts
  constant TOAST_LIMIT (line 5) | const TOAST_LIMIT = 1;
  constant TOAST_REMOVE_DELAY (line 6) | const TOAST_REMOVE_DELAY = 1000000;
  type ToasterToast (line 8) | type ToasterToast = ToastProps & {
  function genId (line 24) | function genId() {
  type ActionType (line 29) | type ActionType = typeof actionTypes;
  type Action (line 31) | type Action =
  type State (line 49) | interface State {
  function dispatch (line 128) | function dispatch(action: Action) {
  type Toast (line 135) | type Toast = Omit<ToasterToast, "id">;
  function toast (line 137) | function toast({ ...props }: Toast) {
  function useToast (line 166) | function useToast() {

FILE: scholarship-finder/src/hooks/useScholarshipSearch.ts
  type ScholarshipUrl (line 5) | interface ScholarshipUrl {
  type AgentStatus (line 11) | interface AgentStatus {
  type SearchState (line 23) | interface SearchState {
  function useScholarshipSearch (line 31) | function useScholarshipSearch() {

FILE: scholarship-finder/src/integrations/supabase/client.ts
  constant SUPABASE_URL (line 5) | const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL;
  constant SUPABASE_PUBLISHABLE_KEY (line 6) | const SUPABASE_PUBLISHABLE_KEY = import.meta.env.VITE_SUPABASE_PUBLISHAB...

FILE: scholarship-finder/src/integrations/supabase/types.ts
  type Json (line 1) | type Json =
  type Database (line 9) | type Database = {
  type DatabaseWithoutInternals (line 34) | type DatabaseWithoutInternals = Omit<Database, "__InternalSupabase">
  type DefaultSchema (line 36) | type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, "p...
  type Tables (line 38) | type Tables<
  type TablesInsert (line 67) | type TablesInsert<
  type TablesUpdate (line 92) | type TablesUpdate<
  type Enums (line 117) | type Enums<
  type CompositeTypes (line 134) | type CompositeTypes<

FILE: scholarship-finder/src/lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {

FILE: scholarship-finder/src/types/scholarship.ts
  type Scholarship (line 1) | interface Scholarship {
  type SearchParams (line 17) | interface SearchParams {
  type SearchResponse (line 23) | interface SearchResponse {

FILE: scholarship-finder/supabase/functions/search-scholarships/index.ts
  type SearchParams (line 6) | interface SearchParams {
  type ScholarshipUrl (line 12) | interface ScholarshipUrl {
  method start (line 27) | async start(controller) {

FILE: silicon-signal/src/app/api/scan/route.ts
  type RiskAnalysis (line 7) | interface RiskAnalysis {
  type ScanResult (line 13) | interface ScanResult {
  type SourceSignal (line 40) | interface SourceSignal {
  type SignalSummary (line 51) | interface SignalSummary {
  type ConfidenceInfo (line 58) | interface ConfidenceInfo {
  constant CACHE_TTL_MS (line 132) | const CACHE_TTL_MS = 0;
  constant MAX_CACHE_ENTRIES (line 133) | const MAX_CACHE_ENTRIES = 200;
  constant SOURCE_TIMEOUT_MS (line 134) | const SOURCE_TIMEOUT_MS = 60000;
  constant FALLBACK_AVAILABILITY (line 135) | const FALLBACK_AVAILABILITY = 'Listed';
  constant FALLBACK_LIFECYCLE (line 136) | const FALLBACK_LIFECYCLE = 'Active';
  constant FALLBACK_PRICE (line 137) | const FALLBACK_PRICE = 'Varies';
  constant SCAN_TIMEOUT_MS (line 138) | const SCAN_TIMEOUT_MS = 110000;
  function POST (line 150) | async function POST(request: Request) {

FILE: silicon-signal/src/app/layout.tsx
  function RootLayout (line 15) | function RootLayout({

FILE: silicon-signal/src/components/Dashboard.tsx
  function Dashboard (line 9) | function Dashboard() {

FILE: silicon-signal/src/components/Header.tsx
  function Header (line 4) | function Header() {

FILE: silicon-signal/src/components/HistoricalTrend.tsx
  function HistoricalTrend (line 4) | function HistoricalTrend({ result }: { result: ScanResult | null }) {

FILE: silicon-signal/src/components/PlatformView.tsx
  function PlatformView (line 13) | function PlatformView() {

FILE: silicon-signal/src/components/RiskBadge.tsx
  type RiskBadgeProps (line 3) | interface RiskBadgeProps {
  function RiskBadge (line 8) | function RiskBadge({ level, score }: RiskBadgeProps) {

FILE: silicon-signal/src/components/ScanForm.tsx
  type ScanFormProps (line 6) | interface ScanFormProps {
  function ScanForm (line 19) | function ScanForm({ onScan, isLoading }: ScanFormProps) {

FILE: silicon-signal/src/components/ScanResultCard.tsx
  function ScanResultCard (line 6) | function ScanResultCard({ result }: { result: ScanResult }) {

FILE: silicon-signal/src/components/SignalOverview.tsx
  function SignalOverview (line 4) | function SignalOverview({ result }: { result: ScanResult | null }) {

FILE: silicon-signal/src/components/SiliconWafer.tsx
  function SiliconWafer (line 5) | function SiliconWafer() {

FILE: silicon-signal/src/components/SystemArchitecture.tsx
  function SystemArchitecture (line 3) | function SystemArchitecture({ active }: { active?: boolean }) {

FILE: silicon-signal/src/components/VendorIntelligence.tsx
  function VendorIntelligence (line 3) | function VendorIntelligence({ result }: { result: ScanResult | null }) {

FILE: silicon-signal/src/lib/store.ts
  function ensureDirectory (line 6) | function ensureDirectory(filePath: string) {
  type HistoricalSnapshot (line 18) | interface HistoricalSnapshot {
  function saveSnapshot (line 27) | function saveSnapshot(partNumber: string, snapshot: HistoricalSnapshot) {
  function getHistory (line 81) | function getHistory(partNumber: string): HistoricalSnapshot[] {
  function getLastSnapshot (line 96) | function getLastSnapshot(partNumber: string): HistoricalSnapshot | null {

FILE: silicon-signal/src/types.ts
  type RiskAnalysis (line 1) | interface RiskAnalysis {
  type ScanResult (line 7) | interface ScanResult {
  type SourceSignal (line 39) | interface SourceSignal {
  type SignalSummary (line 50) | interface SignalSummary {
  type ConfidenceInfo (line 57) | interface ConfidenceInfo {

FILE: stay-scout-hub/src/components/AreaCard.tsx
  type AreaCardProps (line 12) | interface AreaCardProps {
  function AreaCard (line 24) | function AreaCard({ result }: AreaCardProps) {
  function StatusBadge (line 244) | function StatusBadge({

FILE: stay-scout-hub/src/components/AreaResultsSection.tsx
  type AreaResultsSectionProps (line 6) | interface AreaResultsSectionProps {
  function AreaResultsSection (line 15) | function AreaResultsSection({ results, isSearching, city, purpose, check...

FILE: stay-scout-hub/src/components/HeroSection.tsx
  function HeroSection (line 36) | function HeroSection() {

FILE: stay-scout-hub/src/components/LiveBrowserPreview.tsx
  type LiveBrowserPreviewProps (line 7) | interface LiveBrowserPreviewProps {
  function LiveBrowserPreview (line 13) | function LiveBrowserPreview({ streamingUrl, platformName, onClose }: Liv...
  type MiniPreviewProps (line 94) | interface MiniPreviewProps {
  function MiniPreview (line 99) | function MiniPreview({ streamingUrl, onClick }: MiniPreviewProps) {

FILE: stay-scout-hub/src/components/NavLink.tsx
  type NavLinkCompatProps (line 5) | interface NavLinkCompatProps extends Omit<NavLinkProps, "className"> {

FILE: stay-scout-hub/src/components/PlatformCard.tsx
  type PlatformCardProps (line 9) | interface PlatformCardProps {
  function PlatformCard (line 28) | function PlatformCard({ result }: PlatformCardProps) {
  function StatusBadge (line 122) | function StatusBadge({ status, available, hasPreview }: { status: Platfo...
  function StatusIcon (line 144) | function StatusIcon({ status, available }: { status: PlatformResult['sta...

FILE: stay-scout-hub/src/components/PurposeSelector.tsx
  type PurposeSelectorProps (line 5) | interface PurposeSelectorProps {
  function PurposeSelector (line 11) | function PurposeSelector({ selected, onSelect, disabled }: PurposeSelect...

FILE: stay-scout-hub/src/components/ResultsSection.tsx
  type ResultsSectionProps (line 6) | interface ResultsSectionProps {
  function ResultsSection (line 11) | function ResultsSection({ results, isSearching }: ResultsSectionProps) {

FILE: stay-scout-hub/src/components/SearchFormV2.tsx
  type SearchFormV2Props (line 14) | interface SearchFormV2Props {
  function SearchFormV2 (line 19) | function SearchFormV2({ onSearch, isSearching }: SearchFormV2Props) {

FILE: stay-scout-hub/src/components/ui/button.tsx
  type ButtonProps (line 36) | interface ButtonProps

FILE: stay-scout-hub/src/hooks/useAreaSearch.ts
  function useAreaSearch (line 5) | function useAreaSearch() {

FILE: stay-scout-hub/src/hooks/useHotelSearch.ts
  function useHotelSearch (line 5) | function useHotelSearch() {

FILE: stay-scout-hub/src/integrations/supabase/client.ts
  constant SUPABASE_URL (line 5) | const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL;
  constant SUPABASE_PUBLISHABLE_KEY (line 6) | const SUPABASE_PUBLISHABLE_KEY = import.meta.env.VITE_SUPABASE_PUBLISHAB...

FILE: stay-scout-hub/src/integrations/supabase/types.ts
  type Json (line 1) | type Json =
  type Database (line 9) | type Database = {
  type DatabaseWithoutInternals (line 34) | type DatabaseWithoutInternals = Omit<Database, "__InternalSupabase">
  type DefaultSchema (line 36) | type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, "p...
  type Tables (line 38) | type Tables<
  type TablesInsert (line 67) | type TablesInsert<
  type TablesUpdate (line 92) | type TablesUpdate<
  type Enums (line 117) | type Enums<
  type CompositeTypes (line 134) | type CompositeTypes<

FILE: stay-scout-hub/src/lib/api/area-search.ts
  constant API_BASE (line 3) | const API_BASE = import.meta.env.VITE_SUPABASE_URL;
  function discoverAreas (line 5) | async function discoverAreas(params: SearchParams): Promise<AreaSuggesti...
  function researchArea (line 23) | function researchArea(

FILE: stay-scout-hub/src/lib/api/hotel-search.ts
  constant API_BASE (line 3) | const API_BASE = import.meta.env.VITE_SUPABASE_URL;
  function discoverPlatforms (line 5) | async function discoverPlatforms(params: SearchParams): Promise<Platform...
  function checkPlatform (line 23) | function checkPlatform(

FILE: stay-scout-hub/src/lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {

FILE: stay-scout-hub/src/types/hotel.ts
  type TripPurpose (line 3) | type TripPurpose =
  type TripPurposeOption (line 12) | interface TripPurposeOption {
  constant TRIP_PURPOSES (line 19) | const TRIP_PURPOSES: TripPurposeOption[] = [
  type AreaSuggestion (line 30) | interface AreaSuggestion {
  type TopHotel (line 39) | interface TopHotel {
  type AreaResearchResult (line 45) | interface AreaResearchResult {
  type SearchParams (line 80) | interface SearchParams {
  type SSEEventType (line 90) | type SSEEventType = 'CONNECTED' | 'STATUS' | 'SCREENSHOT' | 'COMPLETE' |...
  type SSEEvent (line 92) | interface SSEEvent {
  type Platform (line 100) | interface Platform {
  type Hotel (line 106) | interface Hotel {
  type PlatformResult (line 114) | interface PlatformResult {

FILE: stay-scout-hub/supabase/functions/check-platform/index.ts
  constant TINYFISH_API_KEY (line 8) | const TINYFISH_API_KEY = Deno.env.get('TINYFISH_API_KEY');
  constant MINO_API_URL (line 9) | const MINO_API_URL = 'https://agent.tinyfish.ai/v1/automation/run-sse';

FILE: stay-scout-hub/supabase/functions/discover-areas/index.ts
  constant LOVABLE_API_KEY (line 8) | const LOVABLE_API_KEY = Deno.env.get('LOVABLE_API_KEY');
  function getPurposeDescription (line 121) | function getPurposeDescription(purpose: string, customPurpose?: string):...
  function generateFallbackAreas (line 136) | function generateFallbackAreas(city: string, purpose: string) {

FILE: stay-scout-hub/supabase/functions/discover-platforms/index.ts
  constant GEMINI_API_KEY (line 8) | const GEMINI_API_KEY = Deno.env.get('GEMINI_API_KEY');
  function isIndianCity (line 156) | function isIndianCity(city: string): boolean {
  function isAsiaCity (line 160) | function isAsiaCity(city: string): boolean {
  function generateFallbackPlatforms (line 164) | function generateFallbackPlatforms(city: string, guests: number, checkIn...

FILE: stay-scout-hub/supabase/functions/reasearch-area/index.ts
  constant TINYFISH_API_KEY (line 8) | const TINYFISH_API_KEY = Deno.env.get('TINYFISH_API_KEY');
  constant MINO_API_URL (line 9) | const MINO_API_URL = 'https://agent.tinyfish.ai/v1/automation/run-sse';
  function getPurposeDescription (line 230) | function getPurposeDescription(purpose: string): string {
  function parseResearchResult (line 242) | function parseResearchResult(result: any, area: any, city: string) {
  function generateFallbackAnalysis (line 271) | function generateFallbackAnalysis(area: any, city: string, purpose: stri...

FILE: summer-school-finder/src/components/AgentCard.tsx
  type AgentCardProps (line 4) | interface AgentCardProps {
  function AgentCard (line 8) | function AgentCard({ agent }: AgentCardProps) {

FILE: summer-school-finder/src/components/CompareModal.tsx
  type CompareModalProps (line 7) | interface CompareModalProps {
  function CompareModal (line 27) | function CompareModal({ isOpen, onClose, schools }: CompareModalProps) {

FILE: summer-school-finder/src/components/LiveAgentCard.tsx
  type LiveAgentCardProps (line 4) | interface LiveAgentCardProps {
  function LiveAgentCard (line 8) | function LiveAgentCard({ agent }: LiveAgentCardProps) {

FILE: summer-school-finder/src/components/NavLink.tsx
  type NavLinkCompatProps (line 5) | interface NavLinkCompatProps extends Omit<NavLinkProps, "className"> {

FILE: summer-school-finder/src/components/ResultCard.tsx
  type ResultCardProps (line 7) | interface ResultCardProps {
  function ResultCard (line 13) | function ResultCard({ school, isSelected, onSelect }: ResultCardProps) {

FILE: summer-school-finder/src/components/SearchForm.tsx
  type SearchFormProps (line 9) | interface SearchFormProps {
  function SearchForm (line 35) | function SearchForm({ onSearch, isSearching }: SearchFormProps) {

FILE: summer-school-finder/src/components/ui/badge.tsx
  type BadgeProps (line 23) | interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, Varia...
  function Badge (line 25) | function Badge({ className, variant, ...props }: BadgeProps) {

FILE: summer-school-finder/src/components/ui/button.tsx
  type ButtonProps (line 33) | interface ButtonProps

FILE: summer-school-finder/src/components/ui/calendar.tsx
  type CalendarProps (line 8) | type CalendarProps = React.ComponentProps<typeof DayPicker>;
  function Calendar (line 10) | function Calendar({ className, classNames, showOutsideDays = true, ...pr...

FILE: summer-school-finder/src/components/ui/carousel.tsx
  type CarouselApi (line 8) | type CarouselApi = UseEmblaCarouselType[1];
  type UseCarouselParameters (line 9) | type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
  type CarouselOptions (line 10) | type CarouselOptions = UseCarouselParameters[0];
  type CarouselPlugin (line 11) | type CarouselPlugin = UseCarouselParameters[1];
  type CarouselProps (line 13) | type CarouselProps = {
  type CarouselContextProps (line 20) | type CarouselContextProps = {
  function useCarousel (line 31) | function useCarousel() {

FILE: summer-school-finder/src/components/ui/chart.tsx
  constant THEMES (line 7) | const THEMES = { light: "", dark: ".dark" } as const;
  type ChartConfig (line 9) | type ChartConfig = {
  type ChartContextProps (line 16) | type ChartContextProps = {
  function useChart (line 22) | function useChart() {
  function getPayloadConfigFromPayload (line 278) | function getPayloadConfigFromPayload(config: ChartConfig, payload: unkno...

FILE: summer-school-finder/src/components/ui/command.tsx
  type CommandDialogProps (line 24) | interface CommandDialogProps extends DialogProps {}

FILE: summer-school-finder/src/components/ui/form.tsx
  type FormFieldContextValue (line 11) | type FormFieldContextValue<
  type FormItemContextValue (line 56) | type FormItemContextValue = {

FILE: summer-school-finder/src/components/ui/pagination.tsx
  type PaginationLinkProps (line 29) | type PaginationLinkProps = {

FILE: summer-school-finder/src/components/ui/sheet.tsx
  type SheetContentProps (line 50) | interface SheetContentProps

FILE: summer-school-finder/src/components/ui/sidebar.tsx
  constant SIDEBAR_COOKIE_NAME (line 15) | const SIDEBAR_COOKIE_NAME = "sidebar:state";
  constant SIDEBAR_COOKIE_MAX_AGE (line 16) | const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
  constant SIDEBAR_WIDTH (line 17) | const SIDEBAR_WIDTH = "16rem";
  constant SIDEBAR_WIDTH_MOBILE (line 18) | const SIDEBAR_WIDTH_MOBILE = "18rem";
  constant SIDEBAR_WIDTH_ICON (line 19) | const SIDEBAR_WIDTH_ICON = "3rem";
  constant SIDEBAR_KEYBOARD_SHORTCUT (line 20) | const SIDEBAR_KEYBOARD_SHORTCUT = "b";
  type SidebarContext (line 22) | type SidebarContext = {
  function useSidebar (line 34) | function useSidebar() {

FILE: summer-school-finder/src/components/ui/skeleton.tsx
  function Skeleton (line 3) | function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivE...

FILE: summer-school-finder/src/components/ui/sonner.tsx
  type ToasterProps (line 4) | type ToasterProps = React.ComponentProps<typeof Sonner>;

FILE: summer-school-finder/src/components/ui/textarea.tsx
  type TextareaProps (line 5) | interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAre...

FILE: summer-school-finder/src/components/ui/toast.tsx
  type ToastProps (line 97) | type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
  type ToastActionElement (line 99) | type ToastActionElement = React.ReactElement<typeof ToastAction>;

FILE: summer-school-finder/src/components/ui/toaster.tsx
  function Toaster (line 4) | function Toaster() {

FILE: summer-school-finder/src/hooks/use-mobile.tsx
  constant MOBILE_BREAKPOINT (line 3) | const MOBILE_BREAKPOINT = 768;
  function useIsMobile (line 5) | function useIsMobile() {

FILE: summer-school-finder/src/hooks/use-toast.ts
  constant TOAST_LIMIT (line 5) | const TOAST_LIMIT = 1;
  constant TOAST_REMOVE_DELAY (line 6) | const TOAST_REMOVE_DELAY = 1000000;
  type ToasterToast (line 8) | type ToasterToast = ToastProps & {
  function genId (line 24) | function genId() {
  type ActionType (line 29) | type ActionType = typeof actionTypes;
  type Action (line 31) | type Action =
  type State (line 49) | interface State {
  function dispatch (line 128) | function dispatch(action: Action) {
  type Toast (line 135) | type Toast = Omit<ToasterToast, "id">;
  function toast (line 137) | function toast({ ...props }: Toast) {
  function useToast (line 166) | function useToast() {

FILE: summer-school-finder/src/hooks/useSummerSchoolSearch.ts
  function useSummerSchoolSearch (line 5) | function useSummerSchoolSearch() {

FILE: summer-school-finder/src/integrations/supabase/client.ts
  constant SUPABASE_URL (line 5) | const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL;
  constant SUPABASE_PUBLISHABLE_KEY (line 6) | const SUPABASE_PUBLISHABLE_KEY = import.meta.env.VITE_SUPABASE_PUBLISHAB...

FILE: summer-school-finder/src/integrations/supabase/types.ts
  type Json (line 1) | type Json =
  type Database (line 9) | type Database = {
  type DatabaseWithoutInternals (line 34) | type DatabaseWithoutInternals = Omit<Database, "__InternalSupabase">
  type DefaultSchema (line 36) | type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, "p...
  type Tables (line 38) | type Tables<
  type TablesInsert (line 67) | type TablesInsert<
  type TablesUpdate (line 92) | type TablesUpdate<
  type Enums (line 117) | type Enums<
  type CompositeTypes (line 134) | type CompositeTypes<

FILE: summer-school-finder/src/lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {

FILE: summer-school-finder/src/types/summer-school.ts
  type SearchFormData (line 1) | interface SearchFormData {
  type SummerSchool (line 8) | interface SummerSchool {
  type AgentStatus (line 24) | interface AgentStatus {

FILE: tenders-finder/src/components/NavLink.tsx
  type NavLinkCompatProps (line 5) | interface NavLinkCompatProps extends Omit<NavLinkProps, "className"> {

FILE: tenders-finder/src/components/tender/AgentPreviewCard.tsx
  type AgentPreviewCardProps (line 7) | interface AgentPreviewCardProps {
  function AgentPreviewCard (line 12) | function AgentPreviewCard({ agent, onExpandPreview }: AgentPreviewCardPr...

FILE: tenders-finder/src/components/tender/AgentPreviewGrid.tsx
  type AgentPreviewGridProps (line 8) | interface AgentPreviewGridProps {
  function AgentPreviewGrid (line 13) | function AgentPreviewGrid({ agents, sector }: AgentPreviewGridProps) {

FILE: tenders-finder/src/components/tender/CompareButton.tsx
  type CompareButtonProps (line 6) | interface CompareButtonProps {
  function CompareButton (line 11) | function CompareButton({ selectedCount, onCompare }: CompareButtonProps) {

FILE: tenders-finder/src/components/tender/CompareModal.tsx
  type CompareModalProps (line 7) | interface CompareModalProps {
  constant COMPARE_FIELDS (line 13) | const COMPARE_FIELDS: { key: keyof Tender; label: string }[] = [
  function CompareModal (line 27) | function CompareModal({ isOpen, onClose, tenders }: CompareModalProps) {

FILE: tenders-finder/src/components/tender/Header.tsx
  function Header (line 4) | function Header() {

FILE: tenders-finder/src/components/tender/LinkConfigPage.tsx
  type LinkConfigPageProps (line 17) | interface LinkConfigPageProps {
  function LinkConfigPage (line 23) | function LinkConfigPage({ sector, onBack, onStartSearch }: LinkConfigPag...

FILE: tenders-finder/src/components/tender/LiveBrowserModal.tsx
  type LiveBrowserModalProps (line 7) | interface LiveBrowserModalProps {
  function LiveBrowserModal (line 14) | function LiveBrowserModal({ isOpen, streamingUrl, platformName, onClose ...

FILE: tenders-finder/src/components/tender/SectorIcon.tsx
  type SectorIconProps (line 5) | interface SectorIconProps {
  function SectorIcon (line 13) | function SectorIcon({ icon: Icon, label, description, onClick, disabled ...

FILE: tenders-finder/src/components/tender/SectorSelector.tsx
  type SectorSelectorProps (line 15) | interface SectorSelectorProps {
  constant SECTORS (line 20) | const SECTORS: { sector: Sector; icon: typeof Monitor; label: string; de...
  function SectorSelector (line 29) | function SectorSelector({ onSelectSector, disabled }: SectorSelectorProp...

FILE: tenders-finder/src/components/tender/TenderResultCard.tsx
  type TenderResultCardProps (line 7) | interface TenderResultCardProps {
  function TenderResultCard (line 13) | function TenderResultCard({ tender, isSelected, onToggleSelect }: Tender...

FILE: tenders-finder/src/components/tender/TenderResultsList.tsx
  type TenderResultsListProps (line 6) | interface TenderResultsListProps {
  function TenderResultsList (line 13) | function TenderResultsList({

FILE: tenders-finder/src/components/ui/badge.tsx
  type BadgeProps (line 23) | interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, Varia...
  function Badge (line 25) | function Badge({ className, variant, ...props }: BadgeProps) {

FILE: tenders-finder/src/components/ui/button.tsx
  type ButtonProps (line 33) | interface ButtonProps

FILE: tenders-finder/src/components/ui/calendar.tsx
  type CalendarProps (line 8) | type CalendarProps = React.ComponentProps<typeof DayPicker>;
  function Calendar (line 10) | function Calendar({ className, classNames, showOutsideDays = true, ...pr...

FILE: tenders-finder/src/components/ui/carousel.tsx
  type CarouselApi (line 8) | type CarouselApi = UseEmblaCarouselType[1];
  type UseCarouselParameters (line 9) | type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
  type CarouselOptions (line 10) | type CarouselOptions = UseCarouselParameters[0];
  type CarouselPlugin (line 11) | type CarouselPlugin = UseCarouselParameters[1];
  type CarouselProps (line 13) | type CarouselProps = {
  type CarouselContextProps (line 20) | type CarouselContextProps = {
  function useCarousel (line 31) | function useCarousel() {

FILE: tenders-finder/src/components/ui/chart.tsx
  constant THEMES (line 7) | const THEMES = { light: "", dark: ".dark" } as const;
  type ChartConfig (line 9) | type ChartConfig = {
  type ChartContextProps (line 16) | type ChartContextProps = {
  function useChart (line 22) | function useChart() {
  function getPayloadConfigFromPayload (line 278) | function getPayloadConfigFromPayload(config: ChartConfig, payload: unkno...

FILE: tenders-finder/src/components/ui/command.tsx
  type CommandDialogProps (line 24) | interface CommandDialogProps extends DialogProps {}

FILE: tenders-finder/src/components/ui/form.tsx
  type FormFieldContextValue (line 11) | type FormFieldContextValue<
  type FormItemContextValue (line 56) | type FormItemContextValue = {

FILE: tenders-finder/src/components/ui/pagination.tsx
  type PaginationLinkProps (line 29) | type PaginationLinkProps = {

FILE: tenders-finder/src/components/ui/sheet.tsx
  type SheetContentProps (line 50) | interface SheetContentProps

FILE: tenders-finder/src/components/ui/sidebar.tsx
  constant SIDEBAR_COOKIE_NAME (line 15) | const SIDEBAR_COOKIE_NAME = "sidebar:state";
  constant SIDEBAR_COOKIE_MAX_AGE (line 16) | const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
  constant SIDEBAR_WIDTH (line 17) | const SIDEBAR_WIDTH = "16rem";
  constant SIDEBAR_WIDTH_MOBILE (line 18) | const SIDEBAR_WIDTH_MOBILE = "18rem";
  constant SIDEBAR_WIDTH_ICON (line 19) | const SIDEBAR_WIDTH_ICON = "3rem";
  constant SIDEBAR_KEYBOARD_SHORTCUT (line 20) | const SIDEBAR_KEYBOARD_SHORTCUT = "b";
  type SidebarContext (line 22) | type SidebarContext = {
  function useSidebar (line 34) | function useSidebar() {

FILE: tenders-finder/src/components/ui/skeleton.tsx
  function Skeleton (line 3) | function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivE...

FILE: tenders-finder/src/components/ui/sonner.tsx
  type ToasterProps (line 4) | type ToasterProps = React.ComponentProps<typeof Sonner>;

FILE: tenders-finder/src/components/ui/textarea.tsx
  type TextareaProps (line 5) | interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAre...

FILE: tenders-finder/src/components/ui/toast.tsx
  type ToastProps (line 97) | type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
  type ToastActionElement (line 99) | type ToastActionElement = React.ReactElement<typeof ToastAction>;

FILE: tenders-finder/src/components/ui/toaster.tsx
  function Toaster (line 4) | function Toaster() {

FILE: tenders-finder/src/hooks/use-mobile.tsx
  constant MOBILE_BREAKPOINT (line 3) | const MOBILE_BREAKPOINT = 768;
  function useIsMobile (line 5) | function useIsMobile() {

FILE: tenders-finder/src/hooks/use-toast.ts
  constant TOAST_LIMIT (line 5) | const TOAST_LIMIT = 1;
  constant TOAST_REMOVE_DELAY (line 6) | const TOAST_REMOVE_DELAY = 1000000;
  type ToasterToast (line 8) | type ToasterToast = ToastProps & {
  function genId (line 24) | function genId() {
  type ActionType (line 29) | type ActionType = typeof actionTypes;
  type Action (line 31) | type Action =
  type State (line 49) | interface State {
  function dispatch (line 128) | function dispatch(action: Action) {
  type Toast (line 135) | type Toast = Omit<ToasterToast, "id">;
  function toast (line 137) | function toast({ ...props }: Toast) {
  function useToast (line 166) | function useToast() {

FILE: tenders-finder/src/hooks/useTenderSearch.ts
  constant SUPABASE_URL (line 6) | const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL;
  constant SUPABASE_ANON_KEY (line 7) | const SUPABASE_ANON_KEY = import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY;
  function useTenderSearch (line 9) | function useTenderSearch() {

FILE: tenders-finder/src/integrations/supabase/client.ts
  constant SUPABASE_URL (line 5) | const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL;
  constant SUPABASE_PUBLISHABLE_KEY (line 6) | const SUPABASE_PUBLISHABLE_KEY = import.meta.env.VITE_SUPABASE_PUBLISHAB...

FILE: tenders-finder/src/integrations/supabase/types.ts
  type Json (line 1) | type Json =
  type Database (line 9) | type Database = {
  type DatabaseWithoutInternals (line 34) | type DatabaseWithoutInternals = Omit<Database, "__InternalSupabase">
  type DefaultSchema (line 36) | type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, "p...
  type Tables (line 38) | type Tables<
  type TablesInsert (line 67) | type TablesInsert<
  type TablesUpdate (line 92) | type TablesUpdate<
  type Enums (line 117) | type Enums<
  type CompositeTypes (line 134) | type CompositeTypes<

FILE: tenders-finder/src/lib/api/tinyfish.ts
  constant TENDER_SOURCES (line 4) | const TENDER_SOURCES = [
  type TinyFishEventHandler (line 14) | type TinyFishEventHandler = {
  function startTenderSearch (line 21) | async function startTenderSearch(
  function createInitialAgents (line 54) | function createInitialAgents(): AgentState[] {

FILE: tenders-finder/src/lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {

FILE: tenders-finder/src/pages/Index.tsx
  type ViewState (line 13) | type ViewState = 'selector' | 'config' | 'search';

FILE: tenders-finder/src/types/tender.ts
  type Sector (line 1) | type Sector =
  type Tender (line 9) | interface Tender {
  type AgentState (line 26) | interface AgentState {
  type TenderSearchState (line 36) | interface TenderSearchState {

FILE: tenders-finder/supabase/functions/discover-tender-links/index.ts
  constant DEFAULT_LINKS (line 6) | const DEFAULT_LINKS = [

FILE: tenders-finder/supabase/functions/tinyfish-tender-search/index.ts
  method start (line 75) | async start(controller) {

FILE: tinyskills/app/api/identify-sources/route.ts
  function POST (line 4) | async function POST(request: Request) {

FILE: tinyskills/app/api/scrape-sources/route.ts
  function POST (line 9) | async function POST(request: Request) {

FILE: tinyskills/app/api/synthesize/route.ts
  function POST (line 4) | async function POST(request: Request) {

FILE: tinyskills/app/history/page.tsx
  function HistoryPage (line 26) | function HistoryPage() {

FILE: tinyskills/app/layout.tsx
  function RootLayout (line 12) | function RootLayout({

FILE: tinyskills/app/page.tsx
  function AsciiSpinner (line 24) | function AsciiSpinner({ className = "" }: { className?: string }) {
  function HomePage (line 38) | function HomePage() {

FILE: tinyskills/app/settings/page.tsx
  function SettingsPage (line 25) | function SettingsPage() {

FILE: tinyskills/components/skillforge/nav-header.tsx
  function NavHeader (line 9) | function NavHeader() {

FILE: tinyskills/components/skillforge/phase-indicator.tsx
  type PhaseIndicatorProps (line 7) | interface PhaseIndicatorProps {
  function PhaseIndicator (line 17) | function PhaseIndicator({ phase }: PhaseIndicatorProps) {

FILE: tinyskills/components/skillforge/skill-output.tsx
  type SkillOutputProps (line 13) | interface SkillOutputProps {
  function SkillOutput (line 20) | function SkillOutput({

FILE: tinyskills/components/skillforge/skill-preview.tsx
  type SkillPreviewProps (line 8) | interface SkillPreviewProps {
  function SkillPreview (line 13) | function SkillPreview({

FILE: tinyskills/components/skillforge/skill-raw.tsx
  type SkillRawProps (line 5) | interface SkillRawProps {
  function SkillRaw (line 9) | function SkillRaw({ markdown }: SkillRawProps) {

FILE: tinyskills/components/skillforge/skill-sources.tsx
  type SkillSourcesProps (line 22) | interface SkillSourcesProps {
  function SkillSources (line 26) | function SkillSources({ sources }: SkillSourcesProps) {

FILE: tinyskills/components/skillforge/source-progress.tsx
  type SourceProgressProps (line 44) | interface SourceProgressProps {
  function SourceProgress (line 48) | function SourceProgress({ progress }: SourceProgressProps) {
  function SourceProgressCard (line 60) | function SourceProgressCard({ progress }: { progress: ScrapeProgress }) {

FILE: tinyskills/components/skillforge/source-toggles.tsx
  type SourceTogglesProps (line 43) | interface SourceTogglesProps {
  function SourceToggles (line 49) | function SourceToggles({

FILE: tinyskills/components/skillforge/terminal-input.tsx
  type TerminalInputProps (line 7) | interface TerminalInputProps {
  function TerminalInput (line 16) | function TerminalInput({

FILE: tinyskills/components/ui/badge.tsx
  type BadgeProps (line 30) | interface BadgeProps
  function Badge (line 34) | function Badge({ className, variant, ...props }: BadgeProps) {

FILE: tinyskills/components/ui/button.tsx
  function Button (line 39) | function Button({

FILE: tinyskills/components/ui/card.tsx
  function Card (line 5) | function Card({ className, ...props }: React.ComponentProps<"div">) {
  function CardHeader (line 18) | function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
  function CardTitle (line 31) | function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
  function CardDescription (line 41) | function CardDescription({ className, ...props }: React.ComponentProps<"...
  function CardAction (line 51) | function CardAction({ className, ...props }: React.ComponentProps<"div">) {
  function CardContent (line 64) | function CardContent({ className, ...props }: React.ComponentProps<"div"...
  function CardFooter (line 74) | function CardFooter({ className, ...props }: React.ComponentProps<"div">) {

FILE: tinyskills/components/ui/input.tsx
  function Input (line 5) | function Input({ className, type, ...props }: React.ComponentProps<"inpu...

FILE: tinyskills/components/ui/progress.tsx
  function Progress (line 8) | function Progress({

FILE: tinyskills/components/ui/scroll-area.tsx
  function ScrollArea (line 8) | function ScrollArea({
  function ScrollBar (line 31) | function ScrollBar({

FILE: tinyskills/components/ui/sonner.tsx
  type ToasterProps (line 5) | type ToasterProps = React.ComponentProps<typeof Sonner>;
  function Toaster (line 7) | function Toaster({ ...props }: ToasterProps) {

FILE: tinyskills/components/ui/switch.tsx
  function Switch (line 8) | function Switch({

FILE: tinyskills/components/ui/tabs.tsx
  function Tabs (line 8) | function Tabs({
  function TabsList (line 21) | function TabsList({
  function TabsTrigger (line 37) | function TabsTrigger({
  function TabsContent (line 53) | function TabsContent({

FILE: tinyskills/components/ui/tooltip.tsx
  function TooltipProvider (line 8) | function TooltipProvider({
  function Tooltip (line 21) | function Tooltip({
  function TooltipTrigger (line 31) | function TooltipTrigger({
  function TooltipContent (line 37) | function TooltipContent({

FILE: tinyskills/hooks/use-generation.ts
  type GenerationState (line 15) | interface GenerationState {
  function useGeneration (line 37) | function useGeneration(settings: Settings = DEFAULT_SETTINGS) {

FILE: tinyskills/hooks/use-local-storage.ts
  function useLocalStorage (line 5) | function useLocalStorage<T>(

FILE: tinyskills/lib/ai-client.ts
  function createOpenRouterProvider (line 7) | function createOpenRouterProvider() {
  function getModel (line 20) | function getModel(modelId: string = "google/gemini-2.5-flash-lite") {
  type IdentifySourcesResponse (line 37) | type IdentifySourcesResponse = z.infer<typeof identifiedSourcesSchema>;
  function identifySources (line 42) | async function identifySources(
  function topicToSkillName (line 169) | function topicToSkillName(topic: string): string {
  function synthesizeSkill (line 192) | function synthesizeSkill(

FILE: tinyskills/lib/mino-client.ts
  constant MINO_API_URL (line 14) | const MINO_API_URL = "https://agent.tinyfish.ai/v1/automation/run-sse";
  type MinoRequestConfig (line 16) | interface MinoRequestConfig {
  type MinoResponse (line 26) | interface MinoResponse {
  type MinoCallbacks (line 33) | interface MinoCallbacks {
  function runMinoAutomation (line 43) | async function runMinoAutomation(
  function buildScrapeGoal (line 138) | function buildScrapeGoal(
  function parseScrapedContent (line 236) | function parseScrapedContent(result: unknown): string {

FILE: tinyskills/lib/storage.ts
  constant MAX_SKILLS (line 4) | const MAX_SKILLS = 50;
  constant MAX_HISTORY (line 5) | const MAX_HISTORY = 100;
  function getSkills (line 10) | function getSkills(): GeneratedSkill[] {
  function saveSkill (line 24) | function saveSkill(skill: GeneratedSkill): void {
  function deleteSkill (line 53) | function deleteSkill(skillId: string): void {
  function getSkill (line 67) | function getSkill(skillId: string): GeneratedSkill | null {
  type HistoryEntry (line 75) | interface HistoryEntry {
  function getHistory (line 87) | function getHistory(): HistoryEntry[] {
  function addToHistory (line 101) | function addToHistory(entry: HistoryEntry): void {
  function clearHistory (line 120) | function clearHistory(): void {
  function getSettings (line 133) | function getSettings(): Settings {
  function saveSettings (line 151) | function saveSettings(settings: Partial<Settings>): void {
  function clearAllData (line 166) | function clearAllData(): void {
  function exportSkills (line 181) | function exportSkills(): string {
  function importSkills (line 189) | function importSkills(json: string): number {

FILE: tinyskills/lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {
  type MinoEvent (line 9) | interface MinoEvent {
  function parseSSELine (line 24) | function parseSSELine(line: string): MinoEvent | null {
  function isCompleteEvent (line 37) | function isCompleteEvent(event: MinoEvent): boolean {
  function isErrorEvent (line 41) | function isErrorEvent(event: MinoEvent): boolean {
  function formatStepMessage (line 45) | function formatStepMessage(event: MinoEvent): string {
  function isSystemEvent (line 59) | function isSystemEvent(event: MinoEvent): boolean {
  function generateId (line 78) | function generateId(): string {
  function formatDuration (line 83) | function formatDuration(ms: number): string {
  function truncate (line 94) | function truncate(text: string, length: number): string {
  function countWords (line 100) | function countWords(text: 
Condensed preview — 1181 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,689K chars).
[
  {
    "path": ".github/CODEOWNERS",
    "chars": 447,
    "preview": "# This file is managed by Terraform in github-control repository\n# Do not edit this file, all changes will be overwritte"
  },
  {
    "path": ".github/config/.pre-commit-config-template.yaml",
    "chars": 313,
    "preview": "---\nrepos:\n  - repo: \"local\"\n    hooks:\n      - id: \"trufflehog\"\n        name: \"TruffleHog\"\n        description: Detect "
  },
  {
    "path": ".github/workflows/secrets-scanner.yml",
    "chars": 741,
    "preview": "# This file is managed by Terraform in github-control repository\n# Do not edit this file, all changes will be overwritte"
  },
  {
    "path": ".github/workflows/vuln-scanner-pr.yml",
    "chars": 584,
    "preview": "---\nname: OSV-Scanner PR Scan\n\non:  # yamllint disable-line rule:truthy\n  pull_request:\n    branches: [main]\n\npermission"
  },
  {
    "path": ".semgrepignore",
    "chars": 492,
    "preview": "# This file is managed by Terraform in github-control repository\n# Do not edit this file, all changes will be overwritte"
  },
  {
    "path": ".tags.json",
    "chars": 137,
    "preview": "{\n  \"repo-owner\": \"ENG\",\n  \"vanta-dependabot-owner\": \"ENG\",\n  \"vanta-ebs-inspector-owner\": \"ENG\",\n  \"vanta-ecr-container"
  },
  {
    "path": ".yamllint",
    "chars": 401,
    "preview": "# This file is managed by Terraform in github-control repository\n# Do not edit this file, all changes will be overwritte"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 3226,
    "preview": "# Contributing to the TinyFish Cookbook\n\nHello fellow coder! So you have chosen (or been compelled to) add your awesome "
  },
  {
    "path": "LICENSE",
    "chars": 1065,
    "preview": "MIT License\n\nCopyright (c) 2026 TinyFish\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\no"
  },
  {
    "path": "Makefile",
    "chars": 1043,
    "preview": "SHELL := /usr/bin/env bash\n\ninclude $(wildcard makefiles/*)\n\n.PHONY: check-trufflehog\ncheck-trufflehog:\n\t@if ! which tru"
  },
  {
    "path": "Manga-Availability-Finder/README.md",
    "chars": 5312,
    "preview": "# 🔍 Webtoon/Manga Availability Finder\n\n**Live Demo:** [webtoonhunter.lovable.app](https://webtoonhunter.lovable.app)\n\n--"
  },
  {
    "path": "Manga-Availability-Finder/docs/MINO_API_INTEGRATION.md",
    "chars": 12386,
    "preview": "# Mino API Integration Documentation\n\n## Product Architecture Overview\n\nThis application is a **Manga/Webtoon Finder** t"
  },
  {
    "path": "Manga-Availability-Finder/package.json",
    "chars": 2942,
    "preview": "{\n  \"name\": \"vite_react_shadcn_ts\",\n  \"private\": true,\n  \"version\": \"0.0.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev"
  },
  {
    "path": "Manga-Availability-Finder/public/robots.txt",
    "chars": 160,
    "preview": "User-agent: Googlebot\nAllow: /\n\nUser-agent: Bingbot\nAllow: /\n\nUser-agent: Twitterbot\nAllow: /\n\nUser-agent: facebookexter"
  },
  {
    "path": "Manga-Availability-Finder/src/App.tsx",
    "chars": 871,
    "preview": "import { Toaster } from \"@/components/ui/toaster\";\nimport { Toaster as Sonner } from \"@/components/ui/sonner\";\nimport { "
  },
  {
    "path": "Manga-Availability-Finder/src/components/AgentCard.tsx",
    "chars": 5683,
    "preview": "import { ExternalLink, CheckCircle2, XCircle, Loader2, Globe, Eye } from \"lucide-react\";\nimport { cn } from \"@/lib/utils"
  },
  {
    "path": "Manga-Availability-Finder/src/components/NavLink.tsx",
    "chars": 751,
    "preview": "import { NavLink as RouterNavLink, NavLinkProps } from \"react-router-dom\";\nimport { forwardRef } from \"react\";\nimport { "
  },
  {
    "path": "Manga-Availability-Finder/src/components/ResultsSummary.tsx",
    "chars": 5172,
    "preview": "import { CheckCircle2, XCircle, Loader2, BookOpen } from \"lucide-react\";\nimport { AgentStatus } from \"./AgentCard\";\n\nint"
  },
  {
    "path": "Manga-Availability-Finder/src/components/SearchHero.tsx",
    "chars": 4418,
    "preview": "import { useState } from \"react\";\nimport { Search, Sparkles } from \"lucide-react\";\nimport { Button } from \"@/components/"
  },
  {
    "path": "Manga-Availability-Finder/src/components/ui/avatar.tsx",
    "chars": 1365,
    "preview": "import * as React from \"react\";\nimport * as AvatarPrimitive from \"@radix-ui/react-avatar\";\n\nimport { cn } from \"@/lib/ut"
  },
  {
    "path": "Manga-Availability-Finder/src/components/ui/button.tsx",
    "chars": 2476,
    "preview": "import * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"cla"
  },
  {
    "path": "Manga-Availability-Finder/src/components/ui/card.tsx",
    "chars": 1785,
    "preview": "import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Card = React.forwardRef<HTMLDivElement, React."
  },
  {
    "path": "Manga-Availability-Finder/src/hooks/useMangaSearch.ts",
    "chars": 5852,
    "preview": "import { useState, useCallback } from \"react\";\nimport { supabase } from \"@/integrations/supabase/client\";\nimport { Agent"
  },
  {
    "path": "Manga-Availability-Finder/src/integrations/supabase/client.ts",
    "chars": 591,
    "preview": "// This file is automatically generated. Do not edit it directly.\nimport { createClient } from '@supabase/supabase-js';\n"
  },
  {
    "path": "Manga-Availability-Finder/src/integrations/supabase/types.ts",
    "chars": 5033,
    "preview": "export type Json =\n  | string\n  | number\n  | boolean\n  | null\n  | { [key: string]: Json | undefined }\n  | Json[]\n\nexport"
  },
  {
    "path": "Manga-Availability-Finder/src/lib/utils.ts",
    "chars": 169,
    "preview": "import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: C"
  },
  {
    "path": "Manga-Availability-Finder/src/pages/Index.tsx",
    "chars": 3141,
    "preview": "import { SearchHero } from \"@/components/SearchHero\";\nimport { AgentCard } from \"@/components/AgentCard\";\nimport { Resul"
  },
  {
    "path": "Manga-Availability-Finder/src/pages/NotFound.tsx",
    "chars": 727,
    "preview": "import { useLocation } from \"react-router-dom\";\nimport { useEffect } from \"react\";\n\nconst NotFound = () => {\n  const loc"
  },
  {
    "path": "Manga-Availability-Finder/supabase/config.toml",
    "chars": 134,
    "preview": "project_id = \"vodjiazkxpszllbonlck\"\n\n[functions.discover-manga-sites]\nverify_jwt = false\n\n[functions.search-manga]\nverif"
  },
  {
    "path": "Manga-Availability-Finder/supabase/functions/discover-manga-sites/index.ts",
    "chars": 4489,
    "preview": "import { serve } from \"https://deno.land/std@0.168.0/http/server.ts\";\n\nconst corsHeaders = {\n  \"Access-Control-Allow-Ori"
  },
  {
    "path": "Manga-Availability-Finder/supabase/functions/search-manga/index.ts",
    "chars": 5368,
    "preview": "import { serve } from \"https://deno.land/std@0.168.0/http/server.ts\";\n\nconst corsHeaders = {\n  \"Access-Control-Allow-Ori"
  },
  {
    "path": "Manga-Availability-Finder/tsconfig.json",
    "chars": 369,
    "preview": "{\n  \"files\": [],\n  \"references\": [{ \"path\": \"./tsconfig.app.json\" }, { \"path\": \"./tsconfig.node.json\" }],\n  \"compilerOpt"
  },
  {
    "path": "N8N_WorkFlows/Competitor Scout CLI/Competitor Scout via Tinyfish.json",
    "chars": 35799,
    "preview": "{\n  \"name\": \"Competitor Scout via Tinyfish (Form)\",\n  \"nodes\": [\n    {\n      \"parameters\": {\n        \"url\": \"={{ 'https:"
  },
  {
    "path": "N8N_WorkFlows/Competitor Scout CLI/README.md",
    "chars": 8267,
    "preview": "# Competitor Scout (n8n Workflow) — Beginner Setup & Run Guide\n\nThis repository/workflow lets you research competitor fe"
  },
  {
    "path": "N8N_WorkFlows/Daily Product Hunt Tracker/Daily Product Hunt Tracker via Tinyfish.json",
    "chars": 7082,
    "preview": "{\n  \"name\": \"Daily Product Hunt Tracker — TinyFish + Telegram\",\n  \"nodes\": [\n    {\n      \"id\": \"schedule-trigger\",\n     "
  },
  {
    "path": "N8N_WorkFlows/Daily Product Hunt Tracker/README.md",
    "chars": 6127,
    "preview": "# Daily Product Hunt Tracker (n8n Workflow) — Setup & Run Guide\n\nGet a daily Telegram message with the top 5 trending pr"
  },
  {
    "path": "N8N_WorkFlows/Web Research Agent/README.md",
    "chars": 6180,
    "preview": "# Web Research Agent (n8n Workflow) — Setup & Run Guide\n\nChat with an AI agent that scrapes any website and gives you a "
  },
  {
    "path": "N8N_WorkFlows/Web Research Agent/Web Research Agent via Tinyfish.json",
    "chars": 6250,
    "preview": "{\n  \"nodes\": [\n    {\n      \"parameters\": {\n        \"options\": {}\n      },\n      \"id\": \"6b956bb9-d6c3-46dc-abf0-3b253aef9"
  },
  {
    "path": "N8N_WorkFlows/ai-competitor-analysis/README.md",
    "chars": 3803,
    "preview": "# AI Product Idea Generator\n\nReddit pain points → Product Hunt gap analysis → AI-powered product ideas.\n\nThis n8n workfl"
  },
  {
    "path": "N8N_WorkFlows/ai-competitor-analysis/ai-competitor-radar.json",
    "chars": 5535,
    "preview": "{\n  \"name\": \"AI Product Idea Generator — Reddit → Product Hunt → Gemini\",\n  \"nodes\": [\n    {\n      \"parameters\": {},\n   "
  },
  {
    "path": "README.md",
    "chars": 11574,
    "preview": "# The TinyFish Cookbook\n\n<a href=\"https://www.tinyfish.ai/accelerator\">\n  <img width=\"1920\" height=\"1080\" alt=\"Tinyfish "
  },
  {
    "path": "anime-watch-hub/.gitignore",
    "chars": 313,
    "preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n\n# n"
  },
  {
    "path": "anime-watch-hub/README.md",
    "chars": 3166,
    "preview": "# Anime Watch Hub\n\n\n\n**Live** : [https://v0-animefinder.vercel.app/](https://v0-animefinder.vercel.app/)\n\n\n\nAnime Watch "
  },
  {
    "path": "anime-watch-hub/app/api/check-platform/route.ts",
    "chars": 4041,
    "preview": "import { NextRequest } from 'next/server'\n\nexport async function POST(request: NextRequest) {\n  const encoder = new Text"
  },
  {
    "path": "anime-watch-hub/app/api/discover-platforms/route.ts",
    "chars": 6611,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\n\nconst GEMINI_MODELS = [\n  \"gemini-2.5-flash\",\n  \"gemini-2.5-fl"
  },
  {
    "path": "anime-watch-hub/app/globals.css",
    "chars": 4353,
    "preview": "@import 'tailwindcss';\n@import 'tw-animate-css';\n\n@custom-variant dark (&:is(.dark *));\n\n:root {\n  --background: oklch(1"
  },
  {
    "path": "anime-watch-hub/app/layout.tsx",
    "chars": 1158,
    "preview": "import React from \"react\"\nimport type { Metadata } from 'next'\nimport { Geist, Geist_Mono } from 'next/font/google'\nimpo"
  },
  {
    "path": "anime-watch-hub/app/loading.tsx",
    "chars": 52,
    "preview": "export default function Loading() {\n  return null\n}\n"
  },
  {
    "path": "anime-watch-hub/app/page.tsx",
    "chars": 462,
    "preview": "import { Suspense } from 'react'\nimport { AnimeWatchHub } from '@/components/anime-watch-hub'\n\nfunction Loading() {\n  re"
  },
  {
    "path": "anime-watch-hub/components/anime-watch-hub.tsx",
    "chars": 6265,
    "preview": "'use client'\n\nimport React from \"react\"\nimport { useSearchParams } from 'next/navigation'\nimport { useState } from 'reac"
  },
  {
    "path": "anime-watch-hub/components/platform-card.tsx",
    "chars": 4154,
    "preview": "'use client'\n\nimport { MinoAgentState } from '@/lib/types'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/c"
  },
  {
    "path": "anime-watch-hub/components/results-sidebar.tsx",
    "chars": 4227,
    "preview": "'use client'\n\nimport { MinoAgentState } from '@/lib/types'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/c"
  },
  {
    "path": "anime-watch-hub/components/theme-provider.tsx",
    "chars": 292,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport {\n  ThemeProvider as NextThemesProvider,\n  type ThemeProviderProps,\n"
  },
  {
    "path": "anime-watch-hub/components/ui/accordion.tsx",
    "chars": 2054,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as AccordionPrimitive from '@radix-ui/react-accordion'\nimport { Ch"
  },
  {
    "path": "anime-watch-hub/components/ui/alert-dialog.tsx",
    "chars": 3867,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'\n\nimpor"
  },
  {
    "path": "anime-watch-hub/components/ui/alert.tsx",
    "chars": 1617,
    "preview": "import * as React from 'react'\nimport { cva, type VariantProps } from 'class-variance-authority'\n\nimport { cn } from '@/"
  },
  {
    "path": "anime-watch-hub/components/ui/aspect-ratio.tsx",
    "chars": 280,
    "preview": "'use client'\n\nimport * as AspectRatioPrimitive from '@radix-ui/react-aspect-ratio'\n\nfunction AspectRatio({\n  ...props\n}:"
  },
  {
    "path": "anime-watch-hub/components/ui/avatar.tsx",
    "chars": 1099,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as AvatarPrimitive from '@radix-ui/react-avatar'\n\nimport { cn } fr"
  },
  {
    "path": "anime-watch-hub/components/ui/badge.tsx",
    "chars": 1632,
    "preview": "import * as React from 'react'\nimport { Slot } from '@radix-ui/react-slot'\nimport { cva, type VariantProps } from 'class"
  },
  {
    "path": "anime-watch-hub/components/ui/breadcrumb.tsx",
    "chars": 2358,
    "preview": "import * as React from 'react'\nimport { Slot } from '@radix-ui/react-slot'\nimport { ChevronRight, MoreHorizontal } from "
  },
  {
    "path": "anime-watch-hub/components/ui/button-group.tsx",
    "chars": 2212,
    "preview": "import { Slot } from '@radix-ui/react-slot'\nimport { cva, type VariantProps } from 'class-variance-authority'\n\nimport { "
  },
  {
    "path": "anime-watch-hub/components/ui/button.tsx",
    "chars": 2143,
    "preview": "import * as React from 'react'\nimport { Slot } from '@radix-ui/react-slot'\nimport { cva, type VariantProps } from 'class"
  },
  {
    "path": "anime-watch-hub/components/ui/calendar.tsx",
    "chars": 7679,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport {\n  ChevronDownIcon,\n  ChevronLeftIcon,\n  ChevronRightIcon,\n} from '"
  },
  {
    "path": "anime-watch-hub/components/ui/card.tsx",
    "chars": 1990,
    "preview": "import * as React from 'react'\n\nimport { cn } from '@/lib/utils'\n\nfunction Card({ className, ...props }: React.Component"
  },
  {
    "path": "anime-watch-hub/components/ui/carousel.tsx",
    "chars": 5562,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport useEmblaCarousel, {\n  type UseEmblaCarouselType,\n} from 'embla-carou"
  },
  {
    "path": "anime-watch-hub/components/ui/chart.tsx",
    "chars": 9786,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as RechartsPrimitive from 'recharts'\n\nimport { cn } from '@/lib/ut"
  },
  {
    "path": "anime-watch-hub/components/ui/checkbox.tsx",
    "chars": 1227,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as CheckboxPrimitive from '@radix-ui/react-checkbox'\nimport { Chec"
  },
  {
    "path": "anime-watch-hub/components/ui/collapsible.tsx",
    "chars": 800,
    "preview": "'use client'\n\nimport * as CollapsiblePrimitive from '@radix-ui/react-collapsible'\n\nfunction Collapsible({\n  ...props\n}: "
  },
  {
    "path": "anime-watch-hub/components/ui/command.tsx",
    "chars": 4824,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport { Command as CommandPrimitive } from 'cmdk'\nimport { SearchIcon } fr"
  },
  {
    "path": "anime-watch-hub/components/ui/context-menu.tsx",
    "chars": 8282,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as ContextMenuPrimitive from '@radix-ui/react-context-menu'\nimport"
  },
  {
    "path": "anime-watch-hub/components/ui/dialog.tsx",
    "chars": 3985,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as DialogPrimitive from '@radix-ui/react-dialog'\nimport { XIcon } "
  },
  {
    "path": "anime-watch-hub/components/ui/drawer.tsx",
    "chars": 4258,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport { Drawer as DrawerPrimitive } from 'vaul'\n\nimport { cn } from '@/lib"
  },
  {
    "path": "anime-watch-hub/components/ui/dropdown-menu.tsx",
    "chars": 8432,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'\nimpo"
  },
  {
    "path": "anime-watch-hub/components/ui/empty.tsx",
    "chars": 2401,
    "preview": "import { cva, type VariantProps } from 'class-variance-authority'\n\nimport { cn } from '@/lib/utils'\n\nfunction Empty({ cl"
  },
  {
    "path": "anime-watch-hub/components/ui/field.tsx",
    "chars": 6055,
    "preview": "'use client'\n\nimport { useMemo } from 'react'\nimport { cva, type VariantProps } from 'class-variance-authority'\n\nimport "
  },
  {
    "path": "anime-watch-hub/components/ui/form.tsx",
    "chars": 3761,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as LabelPrimitive from '@radix-ui/react-label'\nimport { Slot } fro"
  },
  {
    "path": "anime-watch-hub/components/ui/hover-card.tsx",
    "chars": 1533,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as HoverCardPrimitive from '@radix-ui/react-hover-card'\n\nimport { "
  },
  {
    "path": "anime-watch-hub/components/ui/input-group.tsx",
    "chars": 5031,
    "preview": "'use client'\n\nimport { cva, type VariantProps } from 'class-variance-authority'\n\nimport { cn } from '@/lib/utils'\nimport"
  },
  {
    "path": "anime-watch-hub/components/ui/input-otp.tsx",
    "chars": 2256,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport { OTPInput, OTPInputContext } from 'input-otp'\nimport { MinusIcon } "
  },
  {
    "path": "anime-watch-hub/components/ui/input.tsx",
    "chars": 963,
    "preview": "import * as React from 'react'\n\nimport { cn } from '@/lib/utils'\n\nfunction Input({ className, type, ...props }: React.Co"
  },
  {
    "path": "anime-watch-hub/components/ui/item.tsx",
    "chars": 4503,
    "preview": "import * as React from 'react'\nimport { Slot } from '@radix-ui/react-slot'\nimport { cva, type VariantProps } from 'class"
  },
  {
    "path": "anime-watch-hub/components/ui/kbd.tsx",
    "chars": 863,
    "preview": "import { cn } from '@/lib/utils'\n\nfunction Kbd({ className, ...props }: React.ComponentProps<'kbd'>) {\n  return (\n    <k"
  },
  {
    "path": "anime-watch-hub/components/ui/label.tsx",
    "chars": 612,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as LabelPrimitive from '@radix-ui/react-label'\n\nimport { cn } from"
  },
  {
    "path": "anime-watch-hub/components/ui/menubar.tsx",
    "chars": 8404,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as MenubarPrimitive from '@radix-ui/react-menubar'\nimport { CheckI"
  },
  {
    "path": "anime-watch-hub/components/ui/navigation-menu.tsx",
    "chars": 6651,
    "preview": "import * as React from 'react'\nimport * as NavigationMenuPrimitive from '@radix-ui/react-navigation-menu'\nimport { cva }"
  },
  {
    "path": "anime-watch-hub/components/ui/pagination.tsx",
    "chars": 2713,
    "preview": "import * as React from 'react'\nimport {\n  ChevronLeftIcon,\n  ChevronRightIcon,\n  MoreHorizontalIcon,\n} from 'lucide-reac"
  },
  {
    "path": "anime-watch-hub/components/ui/popover.tsx",
    "chars": 1636,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as PopoverPrimitive from '@radix-ui/react-popover'\n\nimport { cn } "
  },
  {
    "path": "anime-watch-hub/components/ui/progress.tsx",
    "chars": 741,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as ProgressPrimitive from '@radix-ui/react-progress'\n\nimport { cn "
  },
  {
    "path": "anime-watch-hub/components/ui/radio-group.tsx",
    "chars": 1467,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as RadioGroupPrimitive from '@radix-ui/react-radio-group'\nimport {"
  },
  {
    "path": "anime-watch-hub/components/ui/resizable.tsx",
    "chars": 2030,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport { GripVerticalIcon } from 'lucide-react'\nimport * as ResizablePrimit"
  },
  {
    "path": "anime-watch-hub/components/ui/scroll-area.tsx",
    "chars": 1646,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'\n\nimport "
  },
  {
    "path": "anime-watch-hub/components/ui/select.tsx",
    "chars": 6259,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as SelectPrimitive from '@radix-ui/react-select'\nimport { CheckIco"
  },
  {
    "path": "anime-watch-hub/components/ui/separator.tsx",
    "chars": 700,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as SeparatorPrimitive from '@radix-ui/react-separator'\n\nimport { c"
  },
  {
    "path": "anime-watch-hub/components/ui/sheet.tsx",
    "chars": 4092,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as SheetPrimitive from '@radix-ui/react-dialog'\nimport { XIcon } f"
  },
  {
    "path": "anime-watch-hub/components/ui/sidebar.tsx",
    "chars": 21649,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport { Slot } from '@radix-ui/react-slot'\nimport { cva, VariantProps } fr"
  },
  {
    "path": "anime-watch-hub/components/ui/skeleton.tsx",
    "chars": 276,
    "preview": "import { cn } from '@/lib/utils'\n\nfunction Skeleton({ className, ...props }: React.ComponentProps<'div'>) {\n  return (\n "
  },
  {
    "path": "anime-watch-hub/components/ui/slider.tsx",
    "chars": 1990,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as SliderPrimitive from '@radix-ui/react-slider'\n\nimport { cn } fr"
  },
  {
    "path": "anime-watch-hub/components/ui/sonner.tsx",
    "chars": 564,
    "preview": "'use client'\n\nimport { useTheme } from 'next-themes'\nimport { Toaster as Sonner, ToasterProps } from 'sonner'\n\nconst Toa"
  },
  {
    "path": "anime-watch-hub/components/ui/spinner.tsx",
    "chars": 331,
    "preview": "import { Loader2Icon } from 'lucide-react'\n\nimport { cn } from '@/lib/utils'\n\nfunction Spinner({ className, ...props }: "
  },
  {
    "path": "anime-watch-hub/components/ui/switch.tsx",
    "chars": 1174,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as SwitchPrimitive from '@radix-ui/react-switch'\n\nimport { cn } fr"
  },
  {
    "path": "anime-watch-hub/components/ui/table.tsx",
    "chars": 2452,
    "preview": "'use client'\n\nimport * as React from 'react'\n\nimport { cn } from '@/lib/utils'\n\nfunction Table({ className, ...props }: "
  },
  {
    "path": "anime-watch-hub/components/ui/tabs.tsx",
    "chars": 1971,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as TabsPrimitive from '@radix-ui/react-tabs'\n\nimport { cn } from '"
  },
  {
    "path": "anime-watch-hub/components/ui/textarea.tsx",
    "chars": 760,
    "preview": "import * as React from 'react'\n\nimport { cn } from '@/lib/utils'\n\nfunction Textarea({ className, ...props }: React.Compo"
  },
  {
    "path": "anime-watch-hub/components/ui/toast.tsx",
    "chars": 4863,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as ToastPrimitives from '@radix-ui/react-toast'\nimport { cva, type"
  },
  {
    "path": "anime-watch-hub/components/ui/toaster.tsx",
    "chars": 786,
    "preview": "'use client'\n\nimport { useToast } from '@/hooks/use-toast'\nimport {\n  Toast,\n  ToastClose,\n  ToastDescription,\n  ToastPr"
  },
  {
    "path": "anime-watch-hub/components/ui/toggle-group.tsx",
    "chars": 1927,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as ToggleGroupPrimitive from '@radix-ui/react-toggle-group'\nimport"
  },
  {
    "path": "anime-watch-hub/components/ui/toggle.tsx",
    "chars": 1571,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as TogglePrimitive from '@radix-ui/react-toggle'\nimport { cva, typ"
  },
  {
    "path": "anime-watch-hub/components/ui/tooltip.tsx",
    "chars": 1893,
    "preview": "'use client'\n\nimport * as React from 'react'\nimport * as TooltipPrimitive from '@radix-ui/react-tooltip'\n\nimport { cn } "
  },
  {
    "path": "anime-watch-hub/components/ui/use-mobile.tsx",
    "chars": 565,
    "preview": "import * as React from 'react'\n\nconst MOBILE_BREAKPOINT = 768\n\nexport function useIsMobile() {\n  const [isMobile, setIsM"
  },
  {
    "path": "anime-watch-hub/components/ui/use-toast.ts",
    "chars": 3945,
    "preview": "'use client'\n\n// Inspired by react-hot-toast library\nimport * as React from 'react'\n\nimport type { ToastActionElement, T"
  },
  {
    "path": "anime-watch-hub/components.json",
    "chars": 427,
    "preview": "{\n  \"$schema\": \"https://ui.shadcn.com/schema.json\",\n  \"style\": \"new-york\",\n  \"rsc\": true,\n  \"tsx\": true,\n  \"tailwind\": {"
  },
  {
    "path": "anime-watch-hub/docs/mino-api-integration.md",
    "chars": 16301,
    "preview": "# Anime Watch Hub - Mino API Integration Documentation\n\n## Product Architecture Overview\n\nAnime Watch Hub is an applicat"
  },
  {
    "path": "anime-watch-hub/hooks/use-anime-search.ts",
    "chars": 5656,
    "preview": "'use client'\n\nimport { useState, useCallback } from 'react'\nimport { MinoAgentState } from '@/lib/types'\n\ninterface Disc"
  },
  {
    "path": "anime-watch-hub/hooks/use-mobile.ts",
    "chars": 565,
    "preview": "import * as React from 'react'\n\nconst MOBILE_BREAKPOINT = 768\n\nexport function useIsMobile() {\n  const [isMobile, setIsM"
  },
  {
    "path": "anime-watch-hub/hooks/use-toast.ts",
    "chars": 3945,
    "preview": "'use client'\n\n// Inspired by react-hot-toast library\nimport * as React from 'react'\n\nimport type { ToastActionElement, T"
  },
  {
    "path": "anime-watch-hub/lib/types.ts",
    "chars": 1374,
    "preview": "export interface StreamingPlatform {\n  id: string\n  name: string\n  url: string\n  logo?: string\n}\n\nexport interface Platf"
  },
  {
    "path": "anime-watch-hub/lib/utils.ts",
    "chars": 166,
    "preview": "import { clsx, type ClassValue } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: Cla"
  },
  {
    "path": "anime-watch-hub/next.config.mjs",
    "chars": 181,
    "preview": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n  typescript: {\n    ignoreBuildErrors: true,\n  },\n  images"
  },
  {
    "path": "anime-watch-hub/package.json",
    "chars": 2290,
    "preview": "{\n  \"name\": \"my-v0-project\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"build\": \"next build\",\n    \"dev"
  },
  {
    "path": "anime-watch-hub/postcss.config.mjs",
    "chars": 144,
    "preview": "/** @type {import('postcss-load-config').Config} */\nconst config = {\n  plugins: {\n    '@tailwindcss/postcss': {},\n  },\n}"
  },
  {
    "path": "anime-watch-hub/styles/globals.css",
    "chars": 4353,
    "preview": "@import 'tailwindcss';\n@import 'tw-animate-css';\n\n@custom-variant dark (&:is(.dark *));\n\n:root {\n  --background: oklch(1"
  },
  {
    "path": "anime-watch-hub/tsconfig.json",
    "chars": 595,
    "preview": "{\n  \"compilerOptions\": {\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"target\": \"ES6\",\n    \"sk"
  },
  {
    "path": "bestbet/.gitignore",
    "chars": 480,
    "preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
  },
  {
    "path": "bestbet/README.md",
    "chars": 3708,
    "preview": "# BestBet\n\n**Live:** [https://tinyfish-best-bet.vercel.app](https://tinyfish-best-bet.vercel.app)\n\nBestBet helps you fin"
  },
  {
    "path": "bestbet/app/components/MoneyParticle.tsx",
    "chars": 1350,
    "preview": "\"use client\";\n\nimport { useMemo } from \"react\";\nimport { motion } from \"framer-motion\";\nimport Image from \"next/image\";\n"
  },
  {
    "path": "bestbet/app/components/SportsbookSelector.tsx",
    "chars": 6739,
    "preview": "\"use client\";\n\nimport { useState, useRef, useEffect } from \"react\";\nimport { motion, AnimatePresence } from \"framer-moti"
  },
  {
    "path": "bestbet/app/globals.css",
    "chars": 488,
    "preview": "@import \"tailwindcss\";\n\n:root {\n  --background: #ffffff;\n  --foreground: #171717;\n}\n\n@theme inline {\n  --color-backgroun"
  },
  {
    "path": "bestbet/app/layout.tsx",
    "chars": 733,
    "preview": "import type { Metadata } from \"next\";\nimport { Geist, Geist_Mono } from \"next/font/google\";\nimport \"./globals.css\";\n\ncon"
  },
  {
    "path": "bestbet/app/page.tsx",
    "chars": 15388,
    "preview": "\"use client\";\n\nimport { useState, useEffect, useRef, useCallback } from \"react\";\nimport Image from \"next/image\";\nimport "
  },
  {
    "path": "bestbet/app/webagent.ts",
    "chars": 1425,
    "preview": "const ENDPOINT = \"https://agent.tinyfish.ai/v1/automation/run-sse\";\n\nexport type MinoSSECallbacks = {\n  onStreamingUrl?:"
  },
  {
    "path": "bestbet/eslint.config.mjs",
    "chars": 465,
    "preview": "import { defineConfig, globalIgnores } from \"eslint/config\";\nimport nextVitals from \"eslint-config-next/core-web-vitals\""
  },
  {
    "path": "bestbet/next.config.ts",
    "chars": 133,
    "preview": "import type { NextConfig } from \"next\";\n\nconst nextConfig: NextConfig = {\n  /* config options here */\n};\n\nexport default"
  },
  {
    "path": "bestbet/package.json",
    "chars": 686,
    "preview": "{\n  \"name\": \"bestbet\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"build\": \"next"
  },
  {
    "path": "bestbet/postcss.config.mjs",
    "chars": 94,
    "preview": "const config = {\n  plugins: {\n    \"@tailwindcss/postcss\": {},\n  },\n};\n\nexport default config;\n"
  },
  {
    "path": "bestbet/tsconfig.json",
    "chars": 666,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2017\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    "
  },
  {
    "path": "code-reference-finder/.gitignore",
    "chars": 480,
    "preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
  },
  {
    "path": "code-reference-finder/README.md",
    "chars": 6438,
    "preview": "# Code Reference Finder\n\n**Live:** [https://code-reference-finder.vercel.app](https://code-reference-finder.vercel.app)\n"
  },
  {
    "path": "code-reference-finder/extension/background.js",
    "chars": 831,
    "preview": "// Open side panel when extension icon is clicked\nchrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true });\n\n"
  },
  {
    "path": "code-reference-finder/extension/manifest.json",
    "chars": 431,
    "preview": "{\n  \"manifest_version\": 3,\n  \"name\": \"Code Reference Finder\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Find real-world us"
  },
  {
    "path": "code-reference-finder/extension/sidepanel.html",
    "chars": 596,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-sc"
  },
  {
    "path": "code-reference-finder/extension/sidepanel.js",
    "chars": 993,
    "preview": "const appFrame = document.getElementById('app-frame');\nlet lastTimestamp = 0;\n\nfunction injectCode(code) {\n  appFrame.co"
  },
  {
    "path": "code-reference-finder/next.config.ts",
    "chars": 422,
    "preview": "import type { NextConfig } from 'next';\n\nconst nextConfig: NextConfig = {\n  async headers() {\n    return [\n      {\n     "
  },
  {
    "path": "code-reference-finder/package.json",
    "chars": 530,
    "preview": "{\n  \"name\": \"code-reference-finder\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    "
  },
  {
    "path": "code-reference-finder/postcss.config.mjs",
    "chars": 94,
    "preview": "const config = {\n  plugins: {\n    \"@tailwindcss/postcss\": {},\n  },\n};\n\nexport default config;\n"
  },
  {
    "path": "code-reference-finder/src/app/api/analyze/route.ts",
    "chars": 1380,
    "preview": "import { NextRequest, NextResponse } from 'next/server';\nimport { runPipeline } from '@/lib/orchestrator';\n\nexport async"
  },
  {
    "path": "code-reference-finder/src/app/globals.css",
    "chars": 662,
    "preview": "@import \"tailwindcss\";\n\n:root {\n  --background: #09090b;\n  --foreground: #fafafa;\n}\n\n@theme inline {\n  --color-backgroun"
  },
  {
    "path": "code-reference-finder/src/app/layout.tsx",
    "chars": 874,
    "preview": "import type { Metadata } from 'next';\nimport { Geist, Geist_Mono } from 'next/font/google';\nimport './globals.css';\nimpo"
  },
  {
    "path": "code-reference-finder/src/app/page.tsx",
    "chars": 1191,
    "preview": "'use client';\n\nimport { useEffect, useState } from 'react';\nimport { Header } from '@/components/Header';\nimport { CodeI"
  },
  {
    "path": "code-reference-finder/src/components/AgentCard.tsx",
    "chars": 3878,
    "preview": "'use client';\n\nimport { useState, useEffect } from 'react';\nimport { motion } from 'framer-motion';\nimport { Github, Mes"
  },
  {
    "path": "code-reference-finder/src/components/AnalysisSummary.tsx",
    "chars": 2556,
    "preview": "'use client';\n\nimport { Code2, Package, Puzzle, Workflow } from 'lucide-react';\nimport type { CodeAnalysis } from '@/lib"
  },
  {
    "path": "code-reference-finder/src/components/CodeInput.tsx",
    "chars": 3890,
    "preview": "'use client';\n\nimport { useState } from 'react';\nimport { Search, Clipboard } from 'lucide-react';\n\ninterface CodeInputP"
  },
  {
    "path": "code-reference-finder/src/components/Dashboard.tsx",
    "chars": 2489,
    "preview": "'use client';\n\nimport { useState } from 'react';\nimport { PipelineProgress } from './PipelineProgress';\nimport { Analysi"
  },
  {
    "path": "code-reference-finder/src/components/Header.tsx",
    "chars": 886,
    "preview": "'use client';\n\nimport { Code2, RotateCcw } from 'lucide-react';\nimport { APP_NAME } from '@/lib/constants';\n\ninterface H"
  },
  {
    "path": "code-reference-finder/src/components/LiveBrowserPreview.tsx",
    "chars": 4739,
    "preview": "'use client';\n\nimport { useState, useEffect } from 'react';\nimport { motion } from 'framer-motion';\nimport { Monitor, X,"
  },
  {
    "path": "code-reference-finder/src/components/PipelineProgress.tsx",
    "chars": 2316,
    "preview": "'use client';\n\nimport { Check, Loader2 } from 'lucide-react';\nimport type { AppPhase } from '@/lib/types';\n\nconst STEPS:"
  },
  {
    "path": "code-reference-finder/src/components/ReferenceCard.tsx",
    "chars": 4125,
    "preview": "'use client';\n\nimport { useState } from 'react';\nimport { motion } from 'framer-motion';\nimport {\n  Github,\n  MessageCir"
  },
  {
    "path": "code-reference-finder/src/components/ReferenceGrid.tsx",
    "chars": 1693,
    "preview": "'use client';\n\nimport { AnimatePresence } from 'framer-motion';\nimport { AgentCard } from './AgentCard';\nimport { Refere"
  },
  {
    "path": "code-reference-finder/src/context/AppContext.tsx",
    "chars": 4524,
    "preview": "'use client';\n\nimport {\n  createContext,\n  useContext,\n  useReducer,\n  type ReactNode,\n  type Dispatch,\n} from 'react';\n"
  },
  {
    "path": "code-reference-finder/src/hooks/useCodeAnalysis.ts",
    "chars": 4545,
    "preview": "'use client';\n\nimport { useCallback, useRef } from 'react';\nimport { useAppContext } from '@/context/AppContext';\nimport"
  },
  {
    "path": "code-reference-finder/src/lib/constants.ts",
    "chars": 722,
    "preview": "export const MINO_API_URL = 'https://agent.tinyfish.ai/v1/automation/run-sse';\nexport const OPENROUTER_API_URL = 'https:"
  },
  {
    "path": "code-reference-finder/src/lib/goal-builder.ts",
    "chars": 3823,
    "preview": "import type { CodeAnalysis, SearchResult } from './types';\n\nexport function buildGitHubGoal(\n  url: string,\n  analysis: "
  },
  {
    "path": "code-reference-finder/src/lib/mino-client.ts",
    "chars": 2774,
    "preview": "import { MINO_API_URL } from './constants';\nimport type { MinoRequestConfig, MinoCallbacks, MinoSSEEvent } from './types"
  },
  {
    "path": "code-reference-finder/src/lib/openrouter.ts",
    "chars": 4899,
    "preview": "import { OPENROUTER_API_URL, OPENROUTER_MODEL, OPENROUTER_TEMPERATURE } from './constants';\nimport type { CodeAnalysis, "
  },
  {
    "path": "code-reference-finder/src/lib/orchestrator.ts",
    "chars": 4912,
    "preview": "import { analyzeCode, generateSearchQueries } from './openrouter';\nimport { executeSearches } from './search';\nimport { "
  },
  {
    "path": "code-reference-finder/src/lib/search.ts",
    "chars": 4946,
    "preview": "import {\n  GITHUB_API_URL,\n  STACKEXCHANGE_API_URL,\n  GITHUB_RESULTS_PER_QUERY,\n  STACKOVERFLOW_RESULTS_PER_QUERY,\n  MAX"
  },
  {
    "path": "code-reference-finder/src/lib/types.ts",
    "chars": 4071,
    "preview": "// --- Source Platform ---\nexport type SourcePlatform = 'github' | 'stackoverflow';\n\n// --- OpenRouter Analysis Output -"
  },
  {
    "path": "code-reference-finder/tsconfig.json",
    "chars": 670,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2017\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    "
  },
  {
    "path": "competitor-analysis/.gitignore",
    "chars": 480,
    "preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
  },
  {
    "path": "competitor-analysis/.mcp.json",
    "chars": 115,
    "preview": "{\n  \"mcpServers\": {\n    \"supabase\": {\n      \"type\": \"http\",\n      \"url\": \"https://mcp.supabase.com/mcp\"\n    }\n  }\n}"
  },
  {
    "path": "competitor-analysis/CHANGELOG.md",
    "chars": 6750,
    "preview": "# Changelog - Pricing Intelligence Tool\n\n## Overview\n\nThis tool transforms competitive pricing research from manual spre"
  },
  {
    "path": "competitor-analysis/FEATURES.md",
    "chars": 9516,
    "preview": "# Competitive Pricing Intelligence Dashboard\n\n## Overview\n\nA full-featured competitive pricing intelligence tool that he"
  },
  {
    "path": "competitor-analysis/MINO_API_FEEDBACK.md",
    "chars": 7226,
    "preview": "# Mino API Feedback\n\n## Project Context\n**Use Case:** Competitive Pricing Intelligence Dashboard\n**Scope:** Scraping 10-"
  },
  {
    "path": "competitor-analysis/README.md",
    "chars": 4658,
    "preview": "# TinyFish - Competitive Pricing Intelligence Dashboard\n\n**Live Demo:** https://competitor-priceanalysis.vercel.app/\n\nA "
  },
  {
    "path": "competitor-analysis/app/analysis/page.tsx",
    "chars": 17749,
    "preview": "\"use client\";\n\nimport { useEffect, useState, useCallback, useRef } from \"react\";\nimport { useRouter } from \"next/navigat"
  },
  {
    "path": "competitor-analysis/app/api/analyze-pricing/route.ts",
    "chars": 4206,
    "preview": "import { NextRequest, NextResponse } from 'next/server';\nimport { generateStructured } from '@/lib/ai-client';\nimport { "
  },
  {
    "path": "competitor-analysis/app/api/generate-urls/route.ts",
    "chars": 2413,
    "preview": "import { NextRequest, NextResponse } from 'next/server';\nimport { generateStructured } from '@/lib/ai-client';\nimport { "
  },
  {
    "path": "competitor-analysis/app/api/scrape-pricing/route.ts",
    "chars": 16290,
    "preview": "import { NextRequest } from 'next/server';\nimport type { DetailLevel, PricingTier, CompetitorPricing } from '@/types';\n\n"
  },
  {
    "path": "competitor-analysis/app/company/[id]/page.tsx",
    "chars": 18036,
    "preview": "\"use client\";\n\nimport { useParams, useRouter } from \"next/navigation\";\nimport { usePricing } from \"@/lib/pricing-context"
  },
  {
    "path": "competitor-analysis/app/competitors/page.tsx",
    "chars": 17100,
    "preview": "\"use client\";\n\nimport { useState, useEffect } from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport { usePri"
  },
  {
    "path": "competitor-analysis/app/dashboard/page.tsx",
    "chars": 55685,
    "preview": "\"use client\";\n\nimport { useEffect, useState, useCallback, useRef } from \"react\";\nimport {\n  Tooltip,\n  ResponsiveContain"
  },
  {
    "path": "competitor-analysis/app/globals.css",
    "chars": 4412,
    "preview": "@import \"tailwindcss\";\n@import \"tw-animate-css\";\n\n@custom-variant dark (&:is(.dark *));\n\n@theme inline {\n  --color-backg"
  },
  {
    "path": "competitor-analysis/app/layout.tsx",
    "chars": 1010,
    "preview": "import type { Metadata } from \"next\";\nimport { Geist, Geist_Mono } from \"next/font/google\";\nimport \"./globals.css\";\nimpo"
  },
  {
    "path": "competitor-analysis/app/page.tsx",
    "chars": 685,
    "preview": "\"use client\";\n\nimport { useEffect } from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport { Loader2 } from \""
  },
  {
    "path": "competitor-analysis/components/competitor-input.tsx",
    "chars": 10138,
    "preview": "\"use client\";\n\nimport { useState, useCallback, useRef } from \"react\";\nimport { Plus, Minus, Loader2, Play } from \"lucide"
  },
  {
    "path": "competitor-analysis/components/dashboard-layout.tsx",
    "chars": 11001,
    "preview": "\"use client\";\n\nimport { ReactNode, useState, useEffect, useMemo } from \"react\";\nimport {\n  Table2,\n  LayoutGrid,\n  Light"
  },
  {
    "path": "competitor-analysis/components/settings-panel.tsx",
    "chars": 12679,
    "preview": "\"use client\";\n\nimport { useState, useEffect } from \"react\";\nimport { X, Settings, Trash2 } from \"lucide-react\";\nimport {"
  },
  {
    "path": "competitor-analysis/components/spreadsheet-view.tsx",
    "chars": 19692,
    "preview": "\"use client\";\n\nimport { useState, useCallback, useRef, useEffect, Fragment } from \"react\";\nimport { useRouter } from \"ne"
  },
  {
    "path": "competitor-analysis/components/ui/badge.tsx",
    "chars": 1633,
    "preview": "import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class"
  },
  {
    "path": "competitor-analysis/components/ui/button.tsx",
    "chars": 2218,
    "preview": "import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class"
  },
  {
    "path": "competitor-analysis/components/ui/card.tsx",
    "chars": 1987,
    "preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Card({ className, ...props }: React.Component"
  },
  {
    "path": "competitor-analysis/components/ui/dialog.tsx",
    "chars": 3995,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\"\nimport { XIcon } "
  },
  {
    "path": "competitor-analysis/components/ui/dot-pattern.tsx",
    "chars": 1080,
    "preview": "import { useId } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\ninterface DotPatternProps extends React.SVGProps<SVGS"
  },
  {
    "path": "competitor-analysis/components/ui/input.tsx",
    "chars": 962,
    "preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Input({ className, type, ...props }: React.Co"
  },
  {
    "path": "competitor-analysis/components/ui/select.tsx",
    "chars": 6358,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SelectPrimitive from \"@radix-ui/react-select\"\nimport { CheckIco"
  },
  {
    "path": "competitor-analysis/components/ui/separator.tsx",
    "chars": 699,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SeparatorPrimitive from \"@radix-ui/react-separator\"\n\nimport { c"
  },
  {
    "path": "competitor-analysis/components/ui/sheet.tsx",
    "chars": 4090,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SheetPrimitive from \"@radix-ui/react-dialog\"\nimport { XIcon } f"
  },
  {
    "path": "competitor-analysis/components/ui/sidebar.tsx",
    "chars": 21620,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps"
  },
  {
    "path": "competitor-analysis/components/ui/skeleton.tsx",
    "chars": 276,
    "preview": "import { cn } from \"@/lib/utils\"\n\nfunction Skeleton({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n "
  },
  {
    "path": "competitor-analysis/components/ui/table.tsx",
    "chars": 2868,
    "preview": "import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Table = React.forwardRef<HTMLTableElement, Rea"
  },
  {
    "path": "competitor-analysis/components/ui/tabs.tsx",
    "chars": 1969,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as TabsPrimitive from \"@radix-ui/react-tabs\"\n\nimport { cn } from \""
  }
]

// ... and 981 more files (download for full content)

About this extraction

This page contains the full source code of the tinyfish-io/tinyfish-cookbook GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 1181 files (4.2 MB), approximately 1.2M tokens, and a symbol index with 2815 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!