[STG-2276] feat(cli): once-per-install open nudge after cloud search/fetch#2247
[STG-2276] feat(cli): once-per-install open nudge after cloud search/fetch#2247shrey150 wants to merge 2 commits into
Conversation
…fetch After a successful `browse cloud search` or `browse cloud fetch`, print a one-line, once-per-install tip to stderr pointing at `browse open <url>`. Mirrors the update-check/skill-nudge cache-file pattern: marker in config.cacheDir (open-nudge.json), stderr only so stdout JSON stays machine-clean, never fires on failures, disabled via BROWSE_DISABLE_OPEN_NUDGE=1 / BB_DISABLE_OPEN_NUDGE=1 and in CI/tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: 8d70661 The changes in this PR will be included in the next version bump. This PR includes changesets to release 0 packagesWhen changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
1 issue found across 5 files
Confidence score: 4/5
- In
packages/cli/src/lib/open-nudge.ts,maybeNudgeOpencan return a hint even when writing the marker fails, which breaks the once-per-install guarantee and its documented “failure returns null” behavior; merged as-is, users may see repeated nudges and downstream callers may get inconsistent results—returnnullon marker-write failure (or update the contract and call sites) before merging.
Architecture diagram
sequenceDiagram
participant User as User (CLI)
participant CLI as browse CLI
participant CacheDir as Cache Directory
participant SearchAPI as Cloud Search API
participant FetchAPI as Cloud Fetch API
Note over User,FetchAPI: NEW: Once-per-install open nudge flow
User->>CLI: browse cloud search <query>
CLI->>SearchAPI: API call
alt API succeeds
SearchAPI-->>CLI: results
CLI->>CLI: output results to stdout
CLI->>CacheDir: check for open-nudge.json
alt No marker exists
alt Not disabled (env gates pass)
CLI->>CLI: check BROWSE_DISABLE_OPEN_NUDGE, BB_DISABLE_OPEN_NUDGE, CI, NODE_ENV
alt All gates pass
CLI->>CacheDir: write open-nudge.json {"shownAt": "..."}
CLI->>CLI: print tip to stderr
else Any gate blocks
CLI->>CLI: skip nudge
end
end
else Marker exists
CLI->>CLI: skip nudge
end
else API fails
SearchAPI-->>CLI: error
CLI->>CLI: throw / exit 1
Note over CLI,CacheDir: No nudge on failure
end
Note over User,FetchAPI: Same flow via cloud fetch
User->>CLI: browse cloud fetch <url>
CLI->>FetchAPI: API call
alt API succeeds
FetchAPI-->>CLI: result
CLI->>CLI: output result to stdout
CLI->>CacheDir: check for open-nudge.json
alt No marker exists
alt Not disabled (env gates pass)
CLI->>CacheDir: write open-nudge.json
CLI->>CLI: print tip to stderr
else Any gate blocks
CLI->>CLI: skip nudge
end
else Marker exists
CLI->>CLI: skip nudge
end
else API fails
FetchAPI-->>CLI: error
CLI->>CLI: throw / exit 1
Note over CLI,CacheDir: No nudge on failure
end
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
Per cubic review: an unwritable cache dir would otherwise make the "once-per-install" tip fire on every run. writeNudgeMarker now reports success and maybeNudgeOpen returns null when the marker can't land. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Is this the right shape of fix? You're basically trying to cross-promote another command unrelated to the first one. Wouldn't a better fix be them installing the /browse skill via |
|
Re: @shrey150's design question — running each claim against the 30d telemetry (PostHog 1. "Cross-promoting an unrelated command" — the data says 2. "Better fix = 3. "The largest issue is the key requirement → churn" — true for the keyless cohort, already mitigated; NOT true for the cohort this PR targets. Recommendation: the targeting is data-sound, but the system-shape concern is fair — with #2200 landing, every install already gets one first-run breadcrumb. The disciplined move: hold this PR in draft, land #2200 + #2251 ( |
|
Parked in draft per discussion. Re-evaluation criteria (once #2200 + #2251 have ~2–3 weeks of data, around early July): segment installs by |
Summary
Linear: https://linear.app/browserbase/issue/STG-2276/nudge-cloud-searchfetch-users-toward-browse-open
After a successful
browse cloud searchorbrowse cloud fetch, the CLI now prints a one-line, once-per-install tip to stderr:This mirrors the in-flight #2200 skill-nudge mechanism (cache file in oclif
config.cacheDir, stderr-only, best-effort try/catch, sameBROWSE_DISABLE_*/BB_DISABLE_*+ CI/NODE_ENV=testgating) so the two nudges feel like one system — if #2200 lands first this should rebase trivially (no shared files beyond the same conventions).Impact if merged
The single largest activation gap in the funnel: 58% of real users (3,335 of 5,822/30d) never attempt a browser session — they arrive through cloud.search/cloud.fetch (the #1 and #6 commands by real-user adoption) and leave. The data says getting them to one successful open is decisive: 79% of users who get one successful open chain 3+ more successful commands within the hour, local-browser users retain 29x better than cloud-only users (10.5% vs 0.36% multi-day), and a failed first command cuts 7-day retention 12.4x. One stderr-only, once-per-install line after a successful cloud call is the lowest-LOC lever on that 58%: zero stdout pollution for scripts/agents, fires exactly once.
Implementation notes
packages/cli/src/lib/open-nudge.ts:maybeNudgeOpen({ cacheFile })returns the hint and writes the marker if absent, elsenull;writeOpenNudge(cacheDir)is the command-side wrapper that resolvesopen-nudge.jsonunderconfig.cacheDirand prints to stderr.cloud/search.tsandcloud/fetch.tsonly — API failures throw before the call, so the tip can never fire on errors.BROWSE_DISABLE_UPDATE_CHECK/ [STG-2191] feat(cli): static browse skill nudge + de-spam update notice #2200'sBROWSE_DISABLE_SKILL_NUDGE):BROWSE_DISABLE_OPEN_NUDGE=1/BB_DISABLE_OPEN_NUDGE=1, plus skipped in CI andNODE_ENV=test(mirroring [STG-2191] feat(cli): static browse skill nudge + de-spam update notice #2200's gates verbatim).{"shownAt": ...}) matching theupdate-check.json/skill-nudge.jsoncache-file family.tests/open-nudge.test.tsfollows [STG-2191] feat(cli): static browse skill nudge + de-spam update notice #2200'sskill-nudge.test.tspattern (temp cache file, env injected explicitly).browse, same shape as [STG-2192] fix(cli): make browse skills add failures diagnosable + fail cleanly on unknown skill #2210's.E2E Test Matrix
All rows ran against the local build (
pnpm turbo run build --filter=browse, thennode packages/cli/bin/run.js) with real Browserbase credentials,NODE_ENV=production,CIunset, andBROWSE_CACHE_DIRpointed at a fresh temp dir per scenario.BROWSE_CACHE_DIR=<fresh temp dir> node bin/run.js cloud search "weather today" 1><out.json> 2><err.txt>(live Search API)Tip: open any of these in a live browser — browse open <url> (no API key needed locally).;jq -r '.results[0].url' <out.json>→https://www.weather.gov/(stdout parses as pure JSON); marker written:open-nudge.json={"shownAt":"2026-06-12T19:29:34.435Z"}cloud searchcommand again, same cache dirjq -r '.query'→weather today)BROWSE_CACHE_DIR=<fresh temp dir> node bin/run.js cloud fetch https://example.com(live Fetch API), then run againjq -r '.statusCode'→200, tip on stderr; 2nd run: exit 0, stderr 0 bytescloud fetchtoo and shares the same marker semantics.cloud searchwithBROWSERBASE_API_KEYunset, fresh cache dirMissing Browserbase API key. ...); no tip; stdout 0 bytes; no marker file createdcloud searchtwice (commit 8d70661)jq -r '.results[0].url'→https://www.weather.gov/; 2nd run: exit 0, stderr 0 bytespnpm vitest run(packages/cli)tests/open-nudge.test.tsnow 5 tests post-fix (marker, opt-out envs, CI/test gating, missing cache file, unwritable marker → no nudge)pnpm run lint(packages/cli: prettier + eslint + tsc --noEmit)🤖 Generated with Claude Code