feat: headed browser with agent overlay, update command, skill management#88
feat: headed browser with agent overlay, update command, skill management#88
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
commit: |
There was a problem hiding this comment.
5 issues found across 34 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/cli/src/commands/add-skill.ts">
<violation number="1" location="apps/cli/src/commands/add-skill.ts:165">
P1: Avoid recursively deleting existing non-symlink skill paths; this can silently remove user data.</violation>
</file>
<file name=".specs/chrome-extension.md">
<violation number="1" location=".specs/chrome-extension.md:128">
P2: The Comlink adapter spec registers wrapped listeners but does not remove the same callback reference, so listener cleanup is broken and can lead to duplicate message handling.</violation>
</file>
<file name="packages/browser/tests/chrome-launcher.test.ts">
<violation number="1" location="packages/browser/tests/chrome-launcher.test.ts:69">
P2: This test claims to verify candidate priority order, but `toHaveBeenCalledWith` does not assert order. Use nth-call assertions so the test fails when lookup sequence changes.</violation>
</file>
<file name="packages/browser/src/utils/parse-devtools-active-port.ts">
<violation number="1" location="packages/browser/src/utils/parse-devtools-active-port.ts:10">
P2: Port validation is too permissive; malformed or out-of-range ports are accepted as valid.</violation>
</file>
<file name="packages/browser/src/browser.ts">
<violation number="1" location="packages/browser/src/browser.ts:280">
P1: CDP-connected sessions are incorrectly marked as internal, so closing a session can shut down the user’s external browser instance.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
There was a problem hiding this comment.
7 issues found across 97 files (changes from recent commits).
Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed. We prioritized the most important files first.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/browser/src/mcp/resources/react/rules/advanced-effect-event-deps.md">
<violation number="1" location="packages/browser/src/mcp/resources/react/rules/advanced-effect-event-deps.md:3">
P2: The rule metadata block is malformed: `title` was turned into a Markdown heading, which breaks frontmatter parsing for this doc.</violation>
</file>
<file name="packages/browser/src/mcp/mcp-session.ts">
<violation number="1" location="packages/browser/src/mcp/mcp-session.ts:469">
P2: Only set tmpVideoPath when the copy succeeds; otherwise it can reference a file that wasn’t created after a copy failure.</violation>
</file>
<file name="packages/browser/src/runtime/overlay/lib/use-polled-positions.ts">
<violation number="1" location="packages/browser/src/runtime/overlay/lib/use-polled-positions.ts:38">
P1: The effect is missing `enabled` and `compute` in its dependencies, which can leave polling stuck off/on or using stale computation logic.</violation>
</file>
<file name="packages/browser/src/mcp/overlay-controller.ts">
<violation number="1" location="packages/browser/src/mcp/overlay-controller.ts:116">
P2: If selector resolution fails, `moveCursorToSelector` returns without updating the overlay label, and `positionCursorForCode` has already returned. This leaves stale/missing action text for many `getByRole`/`getByText` commands.</violation>
</file>
<file name="packages/browser/package.json">
<violation number="1" location="packages/browser/package.json:13">
P3: `mkdir -p` in the npm script is not Windows-compatible, so `pnpm build:css` will fail on Windows shells. Use a Node-based mkdir to keep the build cross-platform.</violation>
</file>
<file name="packages/browser/src/mcp/resources/react/rules/advanced-use-latest.md">
<violation number="1" location="packages/browser/src/mcp/resources/react/rules/advanced-use-latest.md:3">
P2: The `title` frontmatter key was accidentally converted to Markdown heading syntax, so metadata parsers will not read a `title` field.</violation>
</file>
<file name="packages/browser/src/browser.ts">
<violation number="1" location="packages/browser/src/browser.ts:431">
P2: If injectOverlayLabels fails, the agent overlay stays hidden because the hide call is outside the cleanup. Wrap hide/inject/screenshot in the ensuring block so the overlay is always restored.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
| ## title: Do Not Put Effect Events in Dependency Arrays | ||
|
|
||
| impact: LOW | ||
| impactDescription: avoids unnecessary effect re-runs and lint errors | ||
| tags: advanced, hooks, useEffectEvent, dependencies, effects | ||
| --- | ||
|
|
There was a problem hiding this comment.
P2: The rule metadata block is malformed: title was turned into a Markdown heading, which breaks frontmatter parsing for this doc.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/browser/src/mcp/resources/react/rules/advanced-effect-event-deps.md, line 3:
<comment>The rule metadata block is malformed: `title` was turned into a Markdown heading, which breaks frontmatter parsing for this doc.</comment>
<file context>
@@ -1,9 +1,10 @@
---
-title: Do Not Put Effect Events in Dependency Arrays
+
+## title: Do Not Put Effect Events in Dependency Arrays
+
impact: LOW
</file context>
| ## title: Do Not Put Effect Events in Dependency Arrays | |
| impact: LOW | |
| impactDescription: avoids unnecessary effect re-runs and lint errors | |
| tags: advanced, hooks, useEffectEvent, dependencies, effects | |
| --- | |
| title: Do Not Put Effect Events in Dependency Arrays | |
| impact: LOW | |
| impactDescription: avoids unnecessary effect re-runs and lint errors | |
| tags: advanced, hooks, useEffectEvent, dependencies, effects | |
| --- | |
| ## title: useEffectEvent for Stable Callback Refs | ||
|
|
||
| impact: LOW | ||
| impactDescription: prevents effect re-runs | ||
| tags: advanced, hooks, useEffectEvent, refs, optimization |
There was a problem hiding this comment.
P2: The title frontmatter key was accidentally converted to Markdown heading syntax, so metadata parsers will not read a title field.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/browser/src/mcp/resources/react/rules/advanced-use-latest.md, line 3:
<comment>The `title` frontmatter key was accidentally converted to Markdown heading syntax, so metadata parsers will not read a `title` field.</comment>
<file context>
@@ -1,9 +1,10 @@
---
-title: useEffectEvent for Stable Callback Refs
+
+## title: useEffectEvent for Stable Callback Refs
+
impact: LOW
</file context>
| ## title: useEffectEvent for Stable Callback Refs | |
| impact: LOW | |
| impactDescription: prevents effect re-runs | |
| tags: advanced, hooks, useEffectEvent, refs, optimization | |
| title: useEffectEvent for Stable Callback Refs | |
| impact: LOW | |
| impactDescription: prevents effect re-runs | |
| tags: advanced, hooks, useEffectEvent, refs, optimization | |
| --- |
| }, | ||
| "scripts": { | ||
| "build": "node scripts/build-runtime.js", | ||
| "build:css": "mkdir -p dist && pnpm exec tailwindcss -i ./src/runtime/overlay/index.css -o ./dist/overlay.css -m && node scripts/css-rem-to-px.js", |
There was a problem hiding this comment.
P3: mkdir -p in the npm script is not Windows-compatible, so pnpm build:css will fail on Windows shells. Use a Node-based mkdir to keep the build cross-platform.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/browser/package.json, line 13:
<comment>`mkdir -p` in the npm script is not Windows-compatible, so `pnpm build:css` will fail on Windows shells. Use a Node-based mkdir to keep the build cross-platform.</comment>
<file context>
@@ -10,7 +10,8 @@
},
"scripts": {
- "build": "node scripts/build-runtime.js",
+ "build:css": "mkdir -p dist && pnpm exec tailwindcss -i ./src/runtime/overlay/index.css -o ./dist/overlay.css -m && node scripts/css-rem-to-px.js",
+ "build": "pnpm build:css && node scripts/build-runtime.js",
"lint": "vp lint && tsc --noEmit",
</file context>
| "build:css": "mkdir -p dist && pnpm exec tailwindcss -i ./src/runtime/overlay/index.css -o ./dist/overlay.css -m && node scripts/css-rem-to-px.js", | |
| "build:css": "node -e \"require('fs').mkdirSync('dist', { recursive: true })\" && pnpm exec tailwindcss -i ./src/runtime/overlay/index.css -o ./dist/overlay.css -m && node scripts/css-rem-to-px.js", |
| cookieBrowserKeys, | ||
| baseUrls, | ||
| devServerHints, | ||
| replayHost, |
There was a problem hiding this comment.
Missing browserProfile in useEffect dependency array
Medium Severity
The newly added browserProfile state variable is read inside the useEffect callback (passed as profileName: browserProfile) but is not listed in the dependency array. This is a stale closure bug — if the profile preference changes, the effect won't re-trigger, and the execution will use the stale profile value captured at initial render time.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit c900492. Configure here.
There was a problem hiding this comment.
1 issue found across 14 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/cli/src/components/screens/testing-screen.tsx">
<violation number="1" location="apps/cli/src/components/screens/testing-screen.tsx:423">
P2: `browserProfile` is used inside the `useEffect` that triggers execution, but it is missing from the effect dependency array, causing stale profile values when preferences change.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
| changesFor, | ||
| instruction, | ||
| isHeadless: !browserHeaded, | ||
| profileName: browserProfile, |
There was a problem hiding this comment.
P2: browserProfile is used inside the useEffect that triggers execution, but it is missing from the effect dependency array, causing stale profile values when preferences change.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/cli/src/components/screens/testing-screen.tsx, line 423:
<comment>`browserProfile` is used inside the `useEffect` that triggers execution, but it is missing from the effect dependency array, causing stale profile values when preferences change.</comment>
<file context>
@@ -419,6 +420,7 @@ export const TestingScreen = ({
changesFor,
instruction,
isHeadless: !browserHeaded,
+ profileName: browserProfile,
cookieBrowserKeys: [...cookieBrowserKeys],
savedFlow,
</file context>
There was a problem hiding this comment.
1 issue found across 5 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/cli/src/commands/init.ts">
<violation number="1" location="apps/cli/src/commands/init.ts:275">
P2: `expect init -y` is no longer non-interactive because browser mode is always prompted when no mode flag is provided.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
| let browserMode = await promptBrowserMode(flagMode); | ||
|
|
||
| logger.break(); | ||
|
|
||
| if (await Effect.runPromise(hasGitHubRemote)) { | ||
| let setupGithubAction = nonInteractive; | ||
|
|
||
| if (!nonInteractive) { | ||
| const response = await prompts({ | ||
| type: "confirm", | ||
| name: "setupGithubAction", | ||
| message: `Set up ${highlighter.info("GitHub Actions")} to continuously test every PR in CI?`, | ||
| initial: true, | ||
| }); | ||
| setupGithubAction = response.setupGithubAction; | ||
| if (browserMode === "cdp") { | ||
| const cdpAvailable = await handleCdpSetup(Boolean(flagMode)); |
There was a problem hiding this comment.
P2: expect init -y is no longer non-interactive because browser mode is always prompted when no mode flag is provided.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/cli/src/commands/init.ts, line 275:
<comment>`expect init -y` is no longer non-interactive because browser mode is always prompted when no mode flag is provided.</comment>
<file context>
@@ -91,61 +230,60 @@ export const runInit = async (options: InitOptions = {}) => {
- );
- }
+ const flagMode = resolveBrowserModeFromFlags(options);
+ let browserMode = await promptBrowserMode(flagMode);
- logger.break();
</file context>
| let browserMode = await promptBrowserMode(flagMode); | |
| logger.break(); | |
| if (await Effect.runPromise(hasGitHubRemote)) { | |
| let setupGithubAction = nonInteractive; | |
| if (!nonInteractive) { | |
| const response = await prompts({ | |
| type: "confirm", | |
| name: "setupGithubAction", | |
| message: `Set up ${highlighter.info("GitHub Actions")} to continuously test every PR in CI?`, | |
| initial: true, | |
| }); | |
| setupGithubAction = response.setupGithubAction; | |
| if (browserMode === "cdp") { | |
| const cdpAvailable = await handleCdpSetup(Boolean(flagMode)); | |
| let browserMode = options.yes ? (flagMode ?? "cdp") : await promptBrowserMode(flagMode); | |
| if (browserMode === "cdp") { | |
| const cdpAvailable = await handleCdpSetup(Boolean(flagMode) || Boolean(options.yes)); |
|
|
||
| vi.mock("../src/commands/update", () => ({ | ||
| detectPackageManager: vi.fn().mockReturnValue("npm"), | ||
| getGlobalInstallCommand: vi.fn().mockReturnValue(["npm", ["install", "-g", "expect-cli"]]), |
There was a problem hiding this comment.
Test mock returns wrong type for getGlobalInstallCommand
Low Severity
The mock for getGlobalInstallCommand returns a plain array ["npm", ["install", "-g", "expect-cli"]] instead of an InstallCommand object { binary: "npm", args: ["install", "-g", "expect-cli"] }. Since runInstallCommand and formatInstallCommand are also mocked, this doesn't cause test failures currently, but it means tests don't catch regressions if init.ts ever accesses .binary or .args on the command directly.
Reviewed by Cursor Bugbot for commit a73adf2. Configure here.
There was a problem hiding this comment.
1 issue found across 9 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/cli/tests/init-flow.test.ts">
<violation number="1" location="apps/cli/tests/init-flow.test.ts:156">
P2: This test bypasses the prompt path it claims to validate by passing `cdp: true`, so invalid prompt handling is not actually tested.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
| }; | ||
|
|
||
| const maybePromptForSkillUpdate = async () => { | ||
| const projectRoot = process.cwd(); |
There was a problem hiding this comment.
Inconsistent project root resolution in skill update
Medium Severity
maybePromptForSkillUpdate uses process.cwd() for the project root, while all other skill-related functions (runAddSkill, promptSkillInstall, writeExpectConfig) use resolveProjectRoot() which finds the git repository root. When expect update is run from a subdirectory, detectInstalledSkillAgents won't find the skills (installed at the git root), causing the function to silently skip the skill update prompt.
Reviewed by Cursor Bugbot for commit 7380b2e. Configure here.
- Replace --headed/--headless with unified --browser-mode <cdp|headed|headless> - Warn on invalid browser mode values - Add apps/demo: Twitter/X-like social feed (Vite + React + shadcn + Lucide) - Update READMEs with new options table
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
There are 5 total unresolved issues (including 3 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 9c24e87. Configure here.
| node dist/index.js \ | ||
| -u https://gym.ami.construction/dashboard \ | ||
| -m "Test the dashboard. Click around, check that pages load, verify navigation works, and look for any broken UI or errors." \ | ||
| -y |
There was a problem hiding this comment.
Accidentally committed project-specific test script
Low Severity
This script hardcodes a URL to a specific external project (https://gym.ami.construction/dashboard) and a specific test instruction. It appears to be a personal development/debugging script that was unintentionally included in the commit, not a general-purpose project utility.
Reviewed by Cursor Bugbot for commit 9c24e87. Configure here.
- Introduced an array of usage prompts to guide users on testing changes in the browser. - Updated the logUsageGuide function to display these prompts instead of a single message.
- Updated the project root resolution to utilize the Effect library for improved error handling and asynchronous behavior. - Removed the previous synchronous git command execution in favor of a more robust solution in the supervisor package.
- Refactored the mock return values for 'node:os' and 'node:fs' to remove the default property, ensuring compatibility with the latest testing framework updates.
- Added '@effect/platform-node' dependency with version 4.0.0-beta.35. - Updated '@modelcontextprotocol/sdk' dependency to use zod@4.3.6. - Removed outdated zod-to-json-schema dependency version. - Introduced new error type 'AcpSessionCreateError' in agent.ts for improved error handling.
- Removed 'AcpSessionCreateError' from the AgentLayerError type definition to streamline error handling and improve code clarity.
- Updated error handling in AcpAdapter to replace 'AcpSessionCreateError' with 'AcpConnectionInitError' for improved clarity and consistency during connection initialization failures.


Summary
This PR replaces the rrweb-based live replay system with a headed Chrome browser session that renders an in-page agent overlay, adds an
expect updatecommand for CLI self-upgrades, overhauls skill installation logic, and cleans up replay-related dead code throughout the stack.Headed browser + agent overlay (
@expect/browser)New: System Chrome launcher (
chrome-launcher.ts)--remote-debugging-port=0and pollsDevToolsActivePortfor the CDP WebSocket URLparseDevToolsActivePortutility for parsing Chrome's debug port file--no-sandbox/--disable-dev-shm-usagein CIChromeNotFoundError,ChromeLaunchTimeoutErrorNew: In-page overlay (
runtime/overlay/)OverlayControllerservice manages cursor positioning, element highlighting, and action loggingref("...")patterns and CSS selectors from Playwright code to position the cursor on the target elementwithHiddento keep captures cleancss-rem-to-px.jspostprocessor for consistent sizing in injected contextsRemoved: rrweb + live replay system
recorder.ts(rrweb injection),replay-viewer.ts(HTML replay builder),rrvideo.ts(session-to-MP4 converter),live-view-server.ts(SSE server for live replay),viewer-events.tsrecorder.test.ts,rrvideo.test.ts,live-view-server.test.tsrrweb,@rrweb/types,rrweb-player,rrvideodependenciesoverlay.ts→lib/annotation-overlay.ts,performance.tsandscroll-detection.tsmoved underlib/McpSession changes
EXPECT_HEADED_ENV_NAMEreplacesEXPECT_LIVE_VIEW_URL_ENV_NAMEreplayOutputPath,accumulatedReplayEvents, replay polling fiber, andLiveViewHandlefrom session statecleanupcallback andisExternalBrowserflag toBrowserSessionDataMap<string, NetworkEntry[]>keyed bymethod:urlinstead of linear.find()scanDeferredfor proper async coordination instead of fire-and-forgetCloseResultno longer includesreplaySessionPath,reportPath,tmpReplaySessionPath,tmpReportPathcdpUrloption changed fromstring | undefinedtoOption.Option<string>MCP server changes (
server.ts)OverlayController+FileSystemadded to the MCP runtime contextplaywrighttool gains adescriptionparameter — shown as the overlay cursor tooltipclosetool no longer emitsrrweb replay:orrrweb report:linesautodiscovery option removed from theopentool; system Chrome is used automatically when headedmkdirSync/writeFileSyncto EffectFileSystemMCP resources cleanup
effect-patterns/rule.md,effect/rule.md, and 8 reference files undereffect/references/CLI:
expect updatecommandupdatesubcommand:expect update [version]for global CLI upgradesexpect update 0.0.30) and dist-tags (expect update latest)expectskill is outdated and prompts to update itrunUpdateCommand) and sync (runUpdateCommandSync) variants for different contextsgetGlobalInstallCommandandnormalizeVersionSpecifierSkill management overhaul
New:
expect-skill.tsutilitygetExpectSkillStatus()— checks if the skill is installed and compares content against GitHub's latest via raw URL fetchreadInstalledSkill()/fetchLatestSkill()— Effect-based with proper error types (ExpectSkillReadError,ExpectSkillFetchError) and timeoutdetectInstalledSkillAgents()— finds which agents have the skill installedhasInstalledExpectSkill()— replaces the oldisSkillInstalled()check that only looked at.agents/skills/expectversion: "...")Improved
add-skill.tsExpectSkillDownloadErrorand timeoutensureAgentSymlinknow returns"linked" | "already-linked" | string— replaces broken symlinks and non-symlink directories (previously errored)CLI UX changes
Default headed mode
--headedflag removed, replaced with--headlessflagbrowserHeadedpreference now defaults totrue(wasfalse)watchcommand:opts.headless ? false : (opts.headed ?? true)Results screen
RuledBoxguidance section telling users to pressyto copy,sto save,rto rerun,pto post to PRSummarysection showing the session summary when presentPlain-text report (
models.ts)toPlainTextnow includes the session summary text when it differs from the pass/fail count lineExecution prompt (
prompts.ts)<session-summary>inRUN_COMPLETEDnow has detailed instructions: must be a single dense line covering what was verified, bugs found, blockers, learnings, and answers to developer questionsRemoved replay proxy infrastructure
replay-proxy-server.ts(~209 lines),push-step-state.ts(~57 lines),load-replay-events.ts(~42 lines)load-replay-events.test.tsexecution-atom.ts: removedsyncReplayProxy,pushStepState,startReplayProxy, random port picking,replayUrl/localReplayUrlfromExecutionResultextract-close-artifacts.ts: removedreplaySessionPath,replayPath,localReplayUrlfromCloseArtifacts; usesfindLastinstead of.slice().reverse().find()Cookie + browser fixes
Browsers.listfilters out Chromium's"System Profile"— not a real user profile, was causing noiserun-test.ts: removed rrweb-to-MP4 video generation path (RrVideo.convertcall); only Playwright video is used nowreplayHostoption removed from preferences store and all CLI flagsSupervisor changes
ExecuteOptions: removedliveViewUrl, addedisHeadless(passed asEXPECT_HEADED_ENV_NAMEto MCP env)EXPECT_REPLAY_OUTPUT_ENV_NAMEand replay output path construction from executorEXPECT_STATE_DIRimport (no longer needed for replay paths)Other
analytics.ts:Effect.tryPromisefor safer async captureexecution-atom.ts: usesPredicate.isObjectandPredicate.isErrorinstead of manual checksdetect-project.ts,git.ts,ensure-state-dir.ts: minor import/path adjustmentstsconfig.json: added"jsx": "react-jsx"for overlay component compilationpnpm-lock.yaml: updated with removed rrweb deps and addedwhichdependencyTest plan
pnpm typecheckpassespnpm testpasses (new tests for chrome-launcher, parse-devtools-active-port, mcp-session, update command, expect-skill, add-skill)pnpm buildsucceedsNote
Medium Risk
Behavior-changing CLI/setup refactor (browser mode defaults/flags, skill install semantics, new global update path) that can impact developer workflows and installation reliability, though changes are localized to tooling not production runtime.
Overview
CLI setup and runtime flow is reworked to prefer real browser sessions over rrweb replay. The PR removes replay/live-preview plumbing (replay proxy, step-state push, replay artifact parsing/display) and simplifies execution results to only surface Playwright video artifacts.
Init/CLI UX now supports explicit browser connection modes. Adds a persisted
.expect/config.json(cdp/headed/headless), updates flags to--browser-mode/--profile, adds CDP detection/onboarding duringinit, and deprecates the interactive TUI with an in-app warning banner.Skill + install management is overhauled. Skill install switches from per-agent symlinks to copying, adds version/latest checks against GitHub with timeouts and structured errors, and introduces a new
expect update [version]command (with sync variant used by the TUI) that updates the global install and can prompt to update the installed skill. Also removes theauditcommand and standardizes Node built-in imports to namespace form.Reviewed by Cursor Bugbot for commit 020fb70. Bugbot is set up for automated code reviews on this repo. Configure here.