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 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 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 = () => ( } /> {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} } /> ); 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 = { idle: { icon: , label: "Ready", className: "text-muted-foreground bg-muted/50", }, searching: { icon: , label: "Searching...", className: "status-searching", }, found: { icon: , label: "Found", className: "status-found", }, not_found: { icon: , label: "Not Found", className: "status-not-found", }, error: { icon: , label: "Error", className: "status-not-found", }, }; export function AgentCard({ siteName, siteUrl, status, statusMessage, streamingUrl, mangaTitle, }: AgentCardProps) { const config = statusConfig[status]; return (
{/* Scanning effect for searching state */} {status === "searching" && (
)} {/* Header */}

{siteName}

{new URL(siteUrl).hostname}

{config.icon} {config.label}
{/* Browser preview area */}
{streamingUrl && status === "searching" ? ( ================================================ FILE: code-reference-finder/extension/sidepanel.js ================================================ const appFrame = document.getElementById('app-frame'); let lastTimestamp = 0; function injectCode(code) { appFrame.contentWindow.postMessage( { type: 'INJECT_CODE', code: code }, '*' ); } // Check for pending code on load (side panel just opened) appFrame.addEventListener('load', () => { chrome.storage.local.get(['pendingCode', 'pendingTimestamp'], (data) => { if (data.pendingCode && data.pendingTimestamp > lastTimestamp) { lastTimestamp = data.pendingTimestamp; // Small delay to ensure the Next.js app is ready setTimeout(() => injectCode(data.pendingCode), 500); } }); }); // Watch for new code stored by content script or background chrome.storage.onChanged.addListener((changes) => { if (changes.pendingCode && changes.pendingTimestamp) { const newTimestamp = changes.pendingTimestamp.newValue; if (newTimestamp > lastTimestamp) { lastTimestamp = newTimestamp; injectCode(changes.pendingCode.newValue); } } }); ================================================ FILE: code-reference-finder/next.config.ts ================================================ import type { NextConfig } from 'next'; const nextConfig: NextConfig = { async headers() { return [ { // Allow Chrome extension to embed this app in an iframe source: '/(.*)', headers: [ { key: 'Content-Security-Policy', value: "frame-ancestors 'self' chrome-extension://*", }, ], }, ]; }, }; export default nextConfig; ================================================ FILE: code-reference-finder/package.json ================================================ { "name": "code-reference-finder", "version": "0.1.0", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start" }, "dependencies": { "framer-motion": "^12.34.0", "lucide-react": "^0.563.0", "next": "16.1.6", "react": "19.2.3", "react-dom": "19.2.3" }, "devDependencies": { "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", "tailwindcss": "^4", "typescript": "^5" } } ================================================ FILE: code-reference-finder/postcss.config.mjs ================================================ const config = { plugins: { "@tailwindcss/postcss": {}, }, }; export default config; ================================================ FILE: code-reference-finder/src/app/api/analyze/route.ts ================================================ import { NextRequest, NextResponse } from 'next/server'; import { runPipeline } from '@/lib/orchestrator'; export async function POST(request: NextRequest) { try { const body = await request.json(); const code = body?.code; if (!code || typeof code !== 'string' || code.trim().length === 0) { return NextResponse.json( { error: 'Missing or empty "code" field in request body' }, { status: 400 } ); } // Create a TransformStream for SSE const { readable, writable } = new TransformStream(); const writer = writable.getWriter(); // Run the pipeline in the background — don't await runPipeline(code.trim(), writer) .catch((err) => { const encoder = new TextEncoder(); const errorEvent = `data: ${JSON.stringify({ type: 'pipeline_error', data: { error: (err as Error).message }, })}\n\n`; writer.write(encoder.encode(errorEvent)).catch(() => {}); }) .finally(() => { writer.close().catch(() => {}); }); return new Response(readable, { headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', Connection: 'keep-alive', }, }); } catch (error) { return NextResponse.json( { error: (error as Error).message }, { status: 500 } ); } } ================================================ FILE: code-reference-finder/src/app/globals.css ================================================ @import "tailwindcss"; :root { --background: #09090b; --foreground: #fafafa; } @theme inline { --color-background: var(--background); --color-foreground: var(--foreground); --font-sans: var(--font-geist-sans); --font-mono: var(--font-geist-mono); } body { background: var(--background); color: var(--foreground); font-family: var(--font-sans), system-ui, -apple-system, sans-serif; } /* Scrollbar styling */ ::-webkit-scrollbar { width: 6px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: #3f3f46; border-radius: 3px; } ::-webkit-scrollbar-thumb:hover { background: #52525b; } ================================================ FILE: code-reference-finder/src/app/layout.tsx ================================================ import type { Metadata } from 'next'; import { Geist, Geist_Mono } from 'next/font/google'; import './globals.css'; import { AppProvider } from '@/context/AppContext'; const geistSans = Geist({ variable: '--font-geist-sans', subsets: ['latin'], }); const geistMono = Geist_Mono({ variable: '--font-geist-mono', subsets: ['latin'], }); export const metadata: Metadata = { title: 'Code Reference Finder', description: 'Find real-world usage examples for unfamiliar code from GitHub and Stack Overflow', }; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( {children} ); } ================================================ FILE: code-reference-finder/src/app/page.tsx ================================================ 'use client'; import { useEffect, useState } from 'react'; import { Header } from '@/components/Header'; import { CodeInput } from '@/components/CodeInput'; import { Dashboard } from '@/components/Dashboard'; import { useCodeAnalysis } from '@/hooks/useCodeAnalysis'; export default function Home() { const { analyze, cancel, reset, state } = useCodeAnalysis(); const [injectedCode, setInjectedCode] = useState(null); // Listen for postMessage from Chrome extension iframe useEffect(() => { const handler = (event: MessageEvent) => { if (event.data?.type === 'INJECT_CODE' && event.data.code) { setInjectedCode(event.data.code); } }; window.addEventListener('message', handler); return () => window.removeEventListener('message', handler); }, []); const isInputPhase = state.phase === 'input'; return (
{isInputPhase ? ( ) : ( )}
); } ================================================ FILE: code-reference-finder/src/components/AgentCard.tsx ================================================ 'use client'; import { useState, useEffect } from 'react'; import { motion } from 'framer-motion'; import { Github, MessageCircle, Loader2, AlertCircle, ChevronDown, ChevronUp } from 'lucide-react'; import { MiniPreview } from './LiveBrowserPreview'; import type { ReferenceAgentState } from '@/lib/types'; interface AgentCardProps { agent: ReferenceAgentState; onPreviewClick: (streamingUrl: string, title: string) => void; } export function AgentCard({ agent, onPreviewClick }: AgentCardProps) { const [showSteps, setShowSteps] = useState(false); const [elapsed, setElapsed] = useState(0); useEffect(() => { if (agent.status === 'complete' || agent.status === 'error') return; if (!agent.startedAt) return; const interval = setInterval(() => { setElapsed(Math.floor((Date.now() - agent.startedAt!) / 1000)); }, 1000); return () => clearInterval(interval); }, [agent.status, agent.startedAt]); const isGitHub = agent.platform === 'github'; const statusColor = agent.status === 'error' ? 'text-red-400' : agent.status === 'complete' ? 'text-green-400' : 'text-blue-400'; return ( {/* Header */}
{isGitHub ? ( ) : ( )} {agent.url.replace('https://', '')}
{agent.status !== 'complete' && agent.status !== 'error' && ( )} {agent.status === 'error' && } {agent.status}
{/* Current step */}

{agent.currentStep}

{/* Elapsed time */} {agent.startedAt && (

{agent.completedAt ? `Completed in ${Math.floor((agent.completedAt - agent.startedAt) / 1000)}s` : `${elapsed}s elapsed`}

)} {/* Error message */} {agent.error && (

{agent.error}

)} {/* Mini preview */} {agent.streamingUrl && agent.status !== 'complete' && agent.status !== 'error' && ( onPreviewClick(agent.streamingUrl!, agent.url)} /> )} {/* Step history toggle */} {agent.steps.length > 0 && ( )} {showSteps && (
{agent.steps.map((step, i) => (

{step.message}

))}
)}
); } ================================================ FILE: code-reference-finder/src/components/AnalysisSummary.tsx ================================================ 'use client'; import { Code2, Package, Puzzle, Workflow } from 'lucide-react'; import type { CodeAnalysis } from '@/lib/types'; interface AnalysisSummaryProps { analysis: CodeAnalysis; } export function AnalysisSummary({ analysis }: AnalysisSummaryProps) { return (
Language: {analysis.language}
{analysis.libraries.length > 0 && (
Libraries:
{analysis.libraries.map((lib) => ( {lib} ))}
)} {analysis.apis.length > 0 && (
APIs:
{analysis.apis.map((api) => ( {api} ))}
)} {analysis.patterns.length > 0 && (
Patterns:
{analysis.patterns.map((p) => ( {p} ))}
)}
); } ================================================ FILE: code-reference-finder/src/components/CodeInput.tsx ================================================ 'use client'; import { useState } from 'react'; import { Search, Clipboard } from 'lucide-react'; interface CodeInputProps { onSubmit: (code: string) => void; injectedCode?: string | null; } const EXAMPLES = [ { label: 'React Query + Axios', code: `import { useQuery } from '@tanstack/react-query'; import axios from 'axios'; const { data, isLoading } = useQuery(['users'], () => axios.get('/api/users').then(res => res.data) );`, }, { label: 'Express Middleware', code: `import express from 'express'; import cors from 'cors'; import helmet from 'helmet'; const app = express(); app.use(cors()); app.use(helmet()); app.use(express.json());`, }, { label: 'Prisma + Next.js', code: `import { PrismaClient } from '@prisma/client'; import { NextResponse } from 'next/server'; const prisma = new PrismaClient(); export async function GET() { const users = await prisma.user.findMany({ include: { posts: true }, }); return NextResponse.json(users); }`, }, ]; export function CodeInput({ onSubmit, injectedCode }: CodeInputProps) { const [code, setCode] = useState(injectedCode ?? ''); const handleSubmit = () => { if (code.trim().length > 10) { onSubmit(code.trim()); } }; const handlePaste = async () => { try { const text = await navigator.clipboard.readText(); setCode(text); } catch { // Clipboard not available } }; return (

Paste Unfamiliar Code

We'll find real-world examples from GitHub repos and Stack Overflow posts that use the same libraries and APIs.