Skip to content

[STG-2276] feat(cli): once-per-install open nudge after cloud search/fetch#2247

Draft
shrey150 wants to merge 2 commits into
mainfrom
shrey/cloud-open-nudge
Draft

[STG-2276] feat(cli): once-per-install open nudge after cloud search/fetch#2247
shrey150 wants to merge 2 commits into
mainfrom
shrey/cloud-open-nudge

Conversation

@shrey150

@shrey150 shrey150 commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Linear: https://linear.app/browserbase/issue/STG-2276/nudge-cloud-searchfetch-users-toward-browse-open

After a successful browse cloud search or browse cloud fetch, the CLI now prints a one-line, once-per-install tip to stderr:

Tip: open any of these in a live browser — browse open <url> (no API key needed locally).

This mirrors the in-flight #2200 skill-nudge mechanism (cache file in oclif config.cacheDir, stderr-only, best-effort try/catch, same BROWSE_DISABLE_*/BB_DISABLE_* + CI/NODE_ENV=test gating) 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

E2E Test Matrix

All rows ran against the local build (pnpm turbo run build --filter=browse, then node packages/cli/bin/run.js) with real Browserbase credentials, NODE_ENV=production, CI unset, and BROWSE_CACHE_DIR pointed at a fresh temp dir per scenario.

Command / flow Observed output Confidence / sufficiency
BROWSE_CACHE_DIR=<fresh temp dir> node bin/run.js cloud search "weather today" 1><out.json> 2><err.txt> (live Search API) exit 0; stderr: 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"} Proves the core feature end-to-end on the live API: tip on stderr, stdout machine-clean (jq parse is the purity proof), marker created.
Same cloud search command again, same cache dir exit 0; stderr 0 bytes; stdout still parses (jq -r '.query'weather today) Proves once-per-install: marker honored, second run completely silent on stderr.
BROWSE_CACHE_DIR=<fresh temp dir> node bin/run.js cloud fetch https://example.com (live Fetch API), then run again 1st run: exit 0, jq -r '.statusCode'200, tip on stderr; 2nd run: exit 0, stderr 0 bytes Proves the nudge fires from cloud fetch too and shares the same marker semantics.
cloud search with BROWSERBASE_API_KEY unset, fresh cache dir exit 1; stderr is only the existing missing-key error (Missing Browserbase API key. ...); no tip; stdout 0 bytes; no marker file created Proves failure paths never nudge and never consume the once-per-install marker.
Post-review-fix re-smoke: fresh cache dir, live cloud search twice (commit 8d70661) 1st run: exit 0, tip on stderr, jq -r '.results[0].url'https://www.weather.gov/; 2nd run: exit 0, stderr 0 bytes Proves the cubic fix (marker-write success gating) didn't regress the live happy path.
pnpm vitest run (packages/cli) 16 files, 217 tests passed pre-fix; tests/open-nudge.test.ts now 5 tests post-fix (marker, opt-out envs, CI/test gating, missing cache file, unwritable marker → no nudge) Supporting: no regressions across the CLI suite; unit-covers the gating matrix the live runs don't (CI, opt-out envs, unwritable cache dir).
pnpm run lint (packages/cli: prettier + eslint + tsc --noEmit) exit 0 Supporting only.

🤖 Generated with Claude Code

…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-bot

changeset-bot Bot commented Jun 12, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8d70661

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 0 packages

When 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

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 5 files

Confidence score: 4/5

  • In packages/cli/src/lib/open-nudge.ts, maybeNudgeOpen can 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—return null on 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
Loading

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread packages/cli/src/lib/open-nudge.ts Outdated
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>
@shrey150

Copy link
Copy Markdown
Contributor Author

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 browse skills install and then having more context on the search API? also, isn't the largest issue that these browse cloud fetch and browse cloud search APIs require a key, which then churns people? can you comment on this with real data to prove/disprove my findings

@shrey150

Copy link
Copy Markdown
Contributor Author

Re: @shrey150's design question — running each claim against the 30d telemetry (PostHog cli/370621, bots + ephemeral fleet excluded; full methodology in the June 11 health report):

1. "Cross-promoting an unrelated command" — the data says open is the retention product, not a random cross-sell.
Cohorts by command mix (5,822 real users/30d): cloud-only = 20,296 incl. fleet (50.9% of installs), median 1 command, 0.36% multi-day return. Local-browser users: 10.46% multi-day — a 29x retention gap. Sharper, on first commands: users whose first command was search and succeeded (63% success) returned at 1.56%; first-open users returned at 12.2% (25.9% when the open succeeded). Even successful search/fetch consumption doesn't retain; a working browser session does — 79% of users who get one successful open chain 3+ more successful commands within the hour. That's the entire thesis of this one-liner: point the consumption surface at the retention surface at the moment of success.

2. "Better fix = browse skills install nudge" — partly agreed, and that's #2200.
The static first-run skill nudge in #2200 now fires for every fresh install on their first command. Skill installers are 7x more engaged (median 12.5 vs 2 commands) and 19x more likely to return (28.4% vs 1.5% multi-day) — self-selected, but directional. The honest open question is whether this PR adds marginal value beyond #2200: they fire at different moments (install-moment vs cloud-success-moment), but nobody has data showing the second breadcrumb converts.

3. "The largest issue is the key requirement → churn" — true for the keyless cohort, already mitigated; NOT true for the cohort this PR targets.
Keyless users: 2,337 distinct users hit missing_api_key/auth_401 in 30d — and the missing-key error already cross-sells browse open <url> --local (lead-with-local, #2192, shipped). But this nudge fires only on successful cloud calls — users with working keys (current-week success: cloud.search 89.3%, cloud.fetch 86.5%). They aren't churning on auth; they succeed, never attempt a session (58% of all real users never do), and don't come back (0.36% multi-day). The key isn't what churns them — the absence of a reason to return is.

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 (first_success telemetry), and let 2–3 weeks of data answer it — if skill-present, cloud-successful installs still never attempt open, land this; if #2200 alone moves the conversion, close it. It's one stderr line, so landing it now and measuring both is also defensible — but measure-first keeps the nudge system minimal. Happy to flip to draft on your 👍.

@shrey150

Copy link
Copy Markdown
Contributor Author

Parked in draft per discussion. Re-evaluation criteria (once #2200 + #2251 have ~2–3 weeks of data, around early July): segment installs by skill_present × cloud-success, and check whether skill-present installs whose first commands are cloud.search/fetch convert to a successful open (first_success) at a materially higher rate than today's baseline (58% of real users never attempt a session; cloud-only multi-day return 0.36%). If #2200 alone moves that conversion → close this. If not → un-draft and land; the branch (shrey/cloud-open-nudge) stays mergeable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant