fix: wire catalog add buttons, fix Trans bold rendering, lint cleanup
Some checks failed
CI / ci (push) Failing after 1m44s
CI / e2e (push) Has been skipped
CI / deploy (push) Has been skipped

- CatalogSearchOverlay: replace handleAddStub with real openAddToCollection/openAddToThread routing based on catalogSearchMode
- ConfirmDialog + __root.tsx: swap t() for Trans component on deleteItemMessage, deleteCandidateMessage, pickWinnerMessage — fixes <bold> rendering as literal text
- Biome format pass: fix 23 lint/format errors across scripts, services, tests
- Planning: mark all UAT and verification gaps resolved for phases 07, 11, 16, 20, 21, 22, 24, 32, 34; close debug sessions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-19 15:36:16 +02:00
parent 16058d0f4d
commit 4ccbb2b070
40 changed files with 807 additions and 227 deletions

View File

@@ -38,7 +38,9 @@ const manufacturerSlug = args["manufacturer"];
const dryRun = args["dry-run"] === "true";
if (!manufacturerSlug) {
console.error("Usage: bun run scripts/crawl-manufacturer.ts --manufacturer=<slug>");
console.error(
"Usage: bun run scripts/crawl-manufacturer.ts --manufacturer=<slug>",
);
process.exit(1);
}
@@ -96,7 +98,9 @@ async function fetchPage(url: string): Promise<string> {
// ── Build system prompt ───────────────────────────────────────────
function buildSystemPrompt(manufacturer: Awaited<ReturnType<typeof fetchManufacturer>>) {
function buildSystemPrompt(
manufacturer: Awaited<ReturnType<typeof fetchManufacturer>>,
) {
return `You are a product data extraction agent for GearBox, a gear management app for bikepacking, cycling, and hiking.
Your task: crawl ${manufacturer.name}'s website (${manufacturer.website}) and extract their complete product catalog.
@@ -148,13 +152,16 @@ type CatalogItem = {
tags: string[];
};
async function runCrawlAgent(manufacturer: Awaited<ReturnType<typeof fetchManufacturer>>): Promise<CatalogItem[]> {
async function runCrawlAgent(
manufacturer: Awaited<ReturnType<typeof fetchManufacturer>>,
): Promise<CatalogItem[]> {
const client = new Anthropic({ apiKey: ANTHROPIC_API_KEY });
const tools: Anthropic.Tool[] = [
{
name: "fetch_page",
description: "Fetch the HTML content of a URL. Use this to explore the manufacturer's website and product pages.",
description:
"Fetch the HTML content of a URL. Use this to explore the manufacturer's website and product pages.",
input_schema: {
type: "object" as const,
properties: {
@@ -221,14 +228,20 @@ async function runCrawlAgent(manufacturer: Awaited<ReturnType<typeof fetchManufa
messages.push({ role: "user", content: toolResults });
}
throw new Error(`Agent exceeded ${MAX_TOOL_ROUNDS} tool rounds without finishing`);
throw new Error(
`Agent exceeded ${MAX_TOOL_ROUNDS} tool rounds without finishing`,
);
}
function parseAgentOutput(text: string): CatalogItem[] {
// Handle agent wrapping output in markdown code blocks
const cleaned = text.replace(/^```json\s*/i, "").replace(/\s*```$/i, "").trim();
const cleaned = text
.replace(/^```json\s*/i, "")
.replace(/\s*```$/i, "")
.trim();
const parsed = JSON.parse(cleaned);
if (!Array.isArray(parsed)) throw new Error("Agent output is not a JSON array");
if (!Array.isArray(parsed))
throw new Error("Agent output is not a JSON array");
return parsed;
}
@@ -269,10 +282,12 @@ async function upsertItems(
throw new Error(`Bulk upsert failed (HTTP ${res.status}): ${err}`);
}
const result = await res.json() as { created: number; updated: number };
const result = (await res.json()) as { created: number; updated: number };
totalCreated += result.created;
totalUpdated += result.updated;
console.log(` batch ${Math.floor(i / 100) + 1}: +${result.created} new, ~${result.updated} updated`);
console.log(
` batch ${Math.floor(i / 100) + 1}: +${result.created} new, ~${result.updated} updated`,
);
}
return { created: totalCreated, updated: totalUpdated };