{"record":{"id":"113d29e8d908a6b2","repo":"garrytan/gstack","slug":"usage-browse-diff-url1-url2","errorCode":null,"errorMessage":"Usage: browse diff <url1> <url2>","messagePattern":"Usage: browse diff <url1> <url2>","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/meta-commands.ts","lineNumber":717,"sourceCode":"            results.push(`[${label}] ${result}`);\n          } catch (err: any) {\n            results.push(`[${label}] ERROR: ${err.message}`);\n          }\n        }\n      }\n\n      // Wait for network to settle after write commands before returning\n      if (lastWasWrite) {\n        await bm.getPage().waitForLoadState('networkidle', { timeout: 2000 }).catch(() => {});\n      }\n\n      return results.join('\\n\\n');\n    }\n\n    // ─── Diff ──────────────────────────────────────────\n    case 'diff': {\n      const [url1, url2] = args;\n      if (!url1 || !url2) throw new Error('Usage: browse diff <url1> <url2>');\n\n      const page = bm.getPage();\n      const normalizedUrl1 = await validateNavigationUrl(url1);\n      await page.goto(normalizedUrl1, { waitUntil: 'domcontentloaded', timeout: 15000 });\n      const text1 = await getCleanText(page);\n\n      const normalizedUrl2 = await validateNavigationUrl(url2);\n      await page.goto(normalizedUrl2, { waitUntil: 'domcontentloaded', timeout: 15000 });\n      const text2 = await getCleanText(page);\n\n      const changes = Diff.diffLines(text1, text2);\n      const output: string[] = [`--- ${url1}`, `+++ ${url2}`, ''];\n\n      for (const part of changes) {\n        const prefix = part.added ? '+' : part.removed ? '-' : ' ';\n        const lines = part.value.split('\\n').filter(l => l.length > 0);\n        for (const line of lines) {\n          output.push(`${prefix} ${line}`);","sourceCodeStart":699,"sourceCodeEnd":735,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/meta-commands.ts#L699-L735","documentation":"The `diff` meta-command requires exactly two URL arguments (`url1`, `url2`) so it can navigate to each in turn and compare their cleaned text via `Diff.diffLines` (lines 715-728). If either argument is missing the command throws this usage error before any network navigation.","triggerScenarios":"Calling `browse diff https://a.example.com` (missing second URL) or `browse diff` with no args (line 716-717).","commonSituations":"Shell quoting that dropped one URL, an agent templating only one URL into the call, or comparing a URL against an empty baseline.","solutions":["Supply both URLs: `browse diff https://prod.example.com https://staging.example.com`.","Quote URLs containing spaces, query params with `&`, or shell metacharacters.","If you intended a single-page snapshot comparison, use `state save`/`state load` or external diff tooling instead."],"exampleFix":"// before\nbrowse diff https://example.com\n// after\nbrowse diff https://example.com https://staging.example.com","handlingStrategy":"validation","validationCode":"if (args.length < 2 || !args[0] || !args[1]) {\n  throw new Error('diff requires exactly two URL arguments');\n}\nif (!/^https?:\\/\\//.test(args[0]) || !/^https?:\\/\\//.test(args[1])) {\n  throw new Error('diff arguments must be absolute http(s) URLs');\n}","typeGuard":"const isUrl = (s: unknown): s is string =>\n  typeof s === 'string' && /^https?:\\/\\//.test(s);","tryCatchPattern":"try { await browse.diff(url1, url2); }\ncatch (err) {\n  if (/Usage: browse diff/.test(err.message)) {\n    // prompt the caller for the missing URL\n  }\n}","preventionTips":["Type the diff wrapper as `(a: string, b: string) => ...` so the compiler enforces arity.","Validate both URLs are absolute http(s) before dispatch."],"tags":["diff","argument-parsing","usage"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}