live-inspect

star 9.8k

Use for one-off / single-question inspection of the running GitLens extension — examining UI state, reading logs, checking feature flags, dispatching a command, or asking "what does the live DOM look like right now". Reference for `vscode-inspector` MCP primitives. For iterative debug-and-fix loops on UI bugs (sweep → fix → re-verify), use `/live-exercise` instead.

gitkraken By gitkraken schedule Updated 6/3/2026

name: live-inspect description: Use for one-off / single-question inspection of the running GitLens extension — examining UI state, reading logs, checking feature flags, dispatching a command, or asking "what does the live DOM look like right now". Reference for vscode-inspector MCP primitives. For iterative debug-and-fix loops on UI bugs (sweep → fix → re-verify), use /live-exercise instead.

/live-inspect — Live Extension Inspection

Launch a real VS Code instance with GitLens loaded, then inspect UI elements, read logs, interact with views, and evaluate runtime values — all programmatically via Playwright.

MCP Server (Preferred for Iterative Inspection)

The vscode-inspector MCP server provides a persistent, interactive session. It launches VS Code once and exposes tools for screenshot/click/inspect/rebuild cycles — much faster than the batch CLI for agentic feedback loops.

The server is auto-discovered via .mcp.json when Claude Code starts in this repo. When connected, these MCP tools are available:

Tool Purpose
launch Start VS Code with GitLens loaded (persistent session)
teardown Close VS Code and clean up
get_status Check if session is running
screenshot Capture window or webview as inline image (capped at 1920px)
execute_command Run any VS Code command by ID
click Click element by CSS selector (main UI or webview)
type_text Type text into inputs
press_key Press keyboard shortcuts
inspect_dom Query DOM elements for text/HTML/attributes/shadowDOM
aria_snapshot Get accessibility tree as YAML (supports webview iframes)
evaluate Run JS in extension host with vscode API
evaluate_in_webview Run JS in webview renderer (DOM, shadow DOM, computed styles)
list_webviews Discover all open webviews with titles, dimensions, content status
wait_for_webview Wait for a webview to finish loading and Lit hydration
read_logs Search extension output logs
read_console Read browser console messages/errors from the main process
resize_window Resize VS Code window content area — only for explicit responsive-breakpoint testing
rebuild_and_reload Build extension + restart extension host

Reading host logs (gotchas)

  • read_logs uses pattern, NOT filter. A wrong/unknown arg is silently ignored and it falls back to pattern: "GitLens" — so you only ever see the activation banner and wrongly conclude "no logs". Always: read_logs({ pattern: "<tag>", last_n }).
  • read_logs DOES capture extension-host console.log/warn/error and GitLens Logger.warn/info/error (info+). It reads the GitLens LogOutputChannel, also on disk at .vscode-test/user-data/logs/<TS>/window1/exthost/eamodio.gitlens/GitLens.log. read_console is webview-only (does not see host logs).
  • @debug/@trace decorator logs are filtered out. The channel defaults to info; GitLens' rich tracing is debug/trace. Console-mirroring (Logger.isDebugging) is gated on ExtensionMode.Development, but the inspector runs in Test mode → off. gitlens.enableDebugLogging (which runs workbench.action.output.activeOutputLogLevel.debug) does NOT take headless. So for ad-hoc host tracing, instrument with Logger.warn('[tag] …') (info+, always written) and read via read_logs({ pattern: "[tag]" }). gitlens.outputLevel is deprecated — don't rely on it.

Typical Workflow

  1. Call launch (once per session — takes ~10s)
  2. Call execute_command to open the view you want to inspect
  3. Call list_webviews to discover open webviews and their exact titles
  4. Call wait_for_webview { webview_title: "<title>" } to wait for Lit hydration
  5. Call screenshot { target: "webview", webview_title: "<title>" } or aria_snapshot { webview_title: "<title>" } to see the current state
  6. Make code changes, then rebuild and reload (see below)
  7. Call screenshot again to verify changes
  8. Repeat steps 6-7 as needed
  9. Call teardown when done

Rebuilding After Code Changes

Extension host code (commands, providers, services, models, parsers — anything under src/ outside src/webviews/apps/):

rebuild_and_reload { build_command: "pnpm run build:extension" }

This restarts the extension host with the new code. All tools continue to work on the same VS Code instance.

Webview code (Lit components, CSS, templates under src/webviews/apps/): No extension host restart needed. Build the webviews, then use the view's refresh command:

rebuild_and_reload { build_command: "pnpm run build:webviews" }
execute_command { command: "gitlens.views.home.refresh" }

Every GitLens webview has a gitlens.views.<name>.refresh command (e.g. gitlens.views.welcome.refresh, gitlens.views.graph.refresh, gitlens.views.commitDetails.refresh). These fully reload the webview with fresh JS/CSS.

Both changed: Use pnpm run build:quick (builds extension + webviews, no linting), then refresh the relevant view.

Quick Examples

Extension host code change:

launch {}
execute_command { command: "gitlens.showHomeView" }
screenshot {}
# ... edit extension host code ...
rebuild_and_reload { build_command: "pnpm run build:extension" }
screenshot {}
teardown

Webview code change:

launch {}
execute_command { command: "gitlens.showWelcomeView" }
inspect_dom { selector: "h1", in_webview: true }
# ... edit webview code ...
rebuild_and_reload { build_command: "pnpm run build:webviews" }
execute_command { command: "gitlens.views.welcome.refresh" }
inspect_dom { selector: "h1", in_webview: true }
teardown

Batch CLI (Fallback for One-Shot Inspection)

scripts/e2e-dev-inspect.mjs — a general-purpose CLI that supports ordered, repeatable actions. Use this when the MCP server is not available or for quick one-off inspections.

Two Modes

Mode Flag ExtensionMode container.debugging gitkraken.env evaluate()
Development (default) (none) Development true ✅ respected
Test --with-evaluator Test false ❌ ignored

Use Development mode when you need gitkraken.env (e.g. testing feature flags against dev API). Use Test mode when you need evaluate() to inspect runtime values (e.g. vscode.env.machineId).

Common Recipes

Inspect any view's DOM content

node scripts/e2e-dev-inspect.mjs --command gitlens.showWelcomeView --query-frame h1

The --query-frame action searches all frames (including nested webview iframes) for matching elements and prints their text content.

Get the full accessibility tree of a view

node scripts/e2e-dev-inspect.mjs --command gitlens.showHomeView --aria

Inspect a specific DOM element

node scripts/e2e-dev-inspect.mjs --command gitlens.showWelcomeView --aria-selector "[class*='header']"

Click something, then inspect the result

node scripts/e2e-dev-inspect.mjs \
  --command gitlens.showHomeView \
  --click-frame "button.start-work" \
  --pause 2000 \
  --query-frame ".dialog-content h2"

Read runtime values (requires --with-evaluator)

node scripts/e2e-dev-inspect.mjs --with-evaluator \
  --eval "vscode.env.machineId" \
  --eval "vscode.version" \
  --eval "vscode.env.appName"

Check feature flag behavior with dev environment

node scripts/e2e-dev-inspect.mjs --env dev \
  --command gitlens.showWelcomeView \
  --query-frame h1 \
  --logs FeatureFlagService

Search extension logs for any pattern

node scripts/e2e-dev-inspect.mjs --logs "error"
node scripts/e2e-dev-inspect.mjs --env dev --logs ConfigCat

Take a screenshot

node scripts/e2e-dev-inspect.mjs --command gitlens.showGraphView --screenshot /tmp/graph.png

Keep VS Code open for manual interaction

node scripts/e2e-dev-inspect.mjs --env dev --keep-open

Add custom settings

node scripts/e2e-dev-inspect.mjs \
  --setting "gitlens.currentLine.enabled=true" \
  --setting "gitlens.hovers.currentLine.over=line" \
  --command gitlens.showWelcomeView --aria

WSL / SSH / Headless Linux

If VS Code is not installed natively in your Linux environment, use --download-vscode to download a portable binary. Xvfb is started automatically if no $DISPLAY is set.

node scripts/e2e-dev-inspect.mjs --download-vscode --command gitlens.showHomeView --aria

Requires xvfb package for headless environments: sudo apt-get install xvfb

How AI Agents Should Use This

Prefer the MCP server for iterative work. Call launch once (use download_vscode: true on WSL/SSH/headless Linux), then use tools in a loop. No output parsing needed — tools return structured results directly.

Choosing the right tool

I want to... MCP tool CLI flag
Discover open webviews list_webviews (N/A)
Wait for a webview to load wait_for_webview (N/A)
Read text from a webview inspect_dom with in_webview: true --query-frame <selector>
See all UI elements and their states aria_snapshot with in_webview: true --aria
Inspect Lit shadow DOM content inspect_dom with property: "shadowDOM" and in_webview (N/A)
Run JS in a webview (DOM/styles/state) evaluate_in_webview (N/A)
Read text from the main VS Code UI inspect_dom --query <selector>
Click a button/link in a webview click with in_webview: true --click-frame <selector>
Read a runtime value (extension host) evaluate --with-evaluator --eval "expr"
Execute a VS Code command execute_command --command <id>
Check extension logs read_logs --logs <pattern>
Check main process console errors read_console { level: "error" } (N/A)
See what the UI looks like screenshot --screenshot <path>
Test a specific responsive breakpoint resize_window (N/A)

GitLens Webview Reference

Command Webview Title Root Element Refresh Command
gitlens.showHomeView Home gl-home-app gitlens.views.home.refresh
gitlens.showWelcomeView Welcome gl-welcome-page (N/A)
gitlens.showGraphPage Commit Graph gl-graph-app gitlens.graph.refresh
gitlens.showGraphView Commit Graph gl-graph-app gitlens.views.graph.refresh
gitlens.showCommitDetailsView Inspect gl-commit-details-app gitlens.views.commitDetails.refresh
(sidebar) Commit Graph Inspect gl-commit-details-app gitlens.views.graphDetails.refresh
gitlens.showTimelinePage Visual History gl-timeline-app gitlens.timeline.refresh
gitlens.showTimelineView Visual File History gl-timeline-app gitlens.views.timeline.refresh
gitlens.showComposerPage Commit Composer gl-composer-app gitlens.composer.refresh
gitlens.showPatchDetailsPage Patch gl-patch-details-app gitlens.patchDetails.refresh
gitlens.showSettingsPage GitLens Settings gl-settings-app gitlens.settings.refresh

Root element tag convention: gl-<name>-app. Use these for inspect_dom selectors and evaluate_in_webview queries.

Inspecting Webview Content

GitLens webviews use Lit web components with Shadow DOM. Here's the recommended approach:

  1. Discover: list_webviews to find open webviews. Output includes index, id (e.g. gitlens.views.commitDetails), title, url, dimensions, and content status. (Or use the reference table above.)
  2. Wait: wait_for_webview { webview_title: "Home" } to ensure Lit hydration is complete
  3. Structure: aria_snapshot { webview_title: "Home" } for the accessibility tree
  4. Shadow DOM: inspect_dom { selector: "gl-home-app", property: "shadowDOM", in_webview: true, webview_title: "Home" } to see rendered Lit templates
  5. JS state: evaluate_in_webview { expression: "document.querySelector('gl-home-app').shadowRoot.querySelector('.my-element').textContent" } to read shadow DOM content. Use .shadowRoot.querySelector() to reach elements inside Lit shadow roots — plain document.querySelector() cannot cross shadow boundaries.
  6. Styles: evaluate_in_webview { expression: "getComputedStyle(document.querySelector('gl-home-app').shadowRoot.querySelector('.my-element')).color" } for computed styles
  7. Errors: read_console { level: "error" } to check for JS errors in the main process. For webview-specific errors, use evaluate_in_webview to inspect state directly.

Targeting a specific webview when multiple are open

When more than one webview is visible (e.g. Graph + Commit Details), webview_title matching can silently fail because outer-frame titles are often empty for unfocused webviews, and the tool falls back to "first webview with content" — which is usually the wrong one. All webview-targeting tools (evaluate_in_webview, wait_for_webview, inspect_dom, aria_snapshot, screenshot, click) accept three matchers:

  • webview_title — outer-frame title (best when titles are reliable; matches the table above)
  • webview_url — case-insensitive substring of the webview URL. Matches the id/purpose/extensionId query params in vscode-webview:// URLs. Use a fragment like "commitDetails" or "graph".
  • webview_index — 0-based index from list_webviews. Deterministic fallback when title and URL matching are insufficient.

Precedence: indexurltitle → first-with-content. Pick webview_url first (intent-revealing); fall back to webview_index if needed.

list_webviews
# returns [{ index: 0, id: "gitlens.views.graph", ... }, { index: 1, id: "gitlens.views.commitDetails", ... }]
evaluate_in_webview { webview_url: "commitDetails", expression: "performance.now()" }

Screenshot Best Practices

Always target a specific webview instead of taking full-window screenshots:

screenshot { target: "webview", webview_title: "Home" }

This captures just the webview content instead of the entire VS Code window. All screenshots are automatically capped at 1920px to stay within Claude's 2000px multi-image limit — no configuration needed.

Use resize_window only when explicitly testing a responsive breakpoint — it resizes the actual Electron window and is clamped by the host display. For larger headless render surfaces, use launch({ screen_resolution }) instead.

Troubleshooting

Webview frame access fails / "not found" errors: Try launch { disable_site_isolation: true }. This disables OOPIF site isolation so Playwright can access webview iframes directly. Note: CORS/CSP are also disabled, so webview behavior may differ slightly from production.

Headless screenshots too small: Use launch { screen_resolution: "2560x1440x24" } for a larger Xvfb display (default: 1920x1080x24).

All Options

Flag Description
--env <env> Set gitkraken.env (e.g. dev, staging)
--with-evaluator Enable HTTP evaluator bridge (Test mode)
--keep-open Keep VS Code running (Ctrl+C to stop)
--setting <key=value> Custom VS Code setting (repeatable)
--wait <ms> Default wait between actions (default 3000)
--activation-wait <ms> Wait time for GitLens activation (default 8000)
--workspace <path> Path to open as workspace
--vscode-path <path> Path to VS Code Electron binary
--download-vscode Download a portable VS Code binary (WSL/SSH/CI)
--flavor <stable|insiders> VS Code variant to auto-detect (default: stable)
--command <cmd> Execute VS Code command
--aria Print full window aria snapshot
--aria-selector <sel> Print aria snapshot of specific element
--query <sel> Print textContent matching selector
--query-frame <sel> Search all frames for selector
--click <sel> Click element
--click-frame <sel> Click inside webview iframe
--screenshot <path> Save screenshot
--logs [pattern] Search extension logs
--eval <expr> Evaluate JS expression in extension host
--pause <ms> Wait specified duration

Exercising Pro-gated features

Pro-gated features (the Commit Graph beyond local repos, Launchpad, Worktrees beyond 1, Cloud Patches, Composer, all AI features, Drafts, Workspaces, etc.) check the user's subscription before unlocking. You can't exercise these without a Paid/Trial subscription on the session.

The extension ships a subscription simulator in DEBUG builds that overrides the session's subscription state without touching the real account.

Setup

execute_command { command: "gitlens.plus.simulate.subscription", args: [{ "state": "Paid", "planId": "pro", "dismissOnboarding": true }] }

Returns true on start, false on stop. Pass dismissOnboarding: true to also pre-dismiss every GitLens onboarding tour/banner (composer welcome, home walkthrough, MCP banner, rebase-editor warning, integration banner, SCM-grouped welcome) — they're full-screen overlays that intercept clicks during automation. State is restored on stop.

State reference

The state field accepts the friendly subscription name (resolves to the numeric SubscriptionState enum at runtime). Most-common picks first:

state Plan options (planId) What it simulates
"Paid" "pro", "advanced", "student", "teams", "enterprise" Active paid subscription — unlocks all Pro features
"Trial" "pro" (default), "advanced", "student" Active trial — unlocks all Pro features for the trial duration
"Community" No account, Community tier (Pro features locked)
"TrialExpired" Account exists, trial used up, no longer eligible
"TrialReactivationEligible" Account exists, trial used up, eligible to reactivate
"VerificationRequired" Account created but email not verified

Optional modifiers:

Modifier Pairs with Effect
reactivatedTrial: true state: "Trial" Reactivated trial (vs. fresh)
expiredPaid: true state: "Paid" Expired paid (downgrades to Community at the gate)
featurePreviews: { day, durationSeconds } state: "Community" Pro Preview window. day: 0 = day 1, 1 = day 2, 2 = day 3, proFeaturePreviewUsages = expired. durationSeconds: window timeout
dismissOnboarding: true any Pre-dismiss onboarding overlays (see Setup)

Common recipes

For other states, swap state/planId/modifiers per the tables above — the start-command shape is the same.

Pro user (default for most pro-feature testing):

execute_command { command: "gitlens.plus.simulate.subscription", args: [{ "state": "Paid", "planId": "pro", "dismissOnboarding": true }] }

Community gate (paywall UX):

execute_command { command: "gitlens.plus.simulate.subscription", args: [{ "state": "Community", "dismissOnboarding": true }] }

Feature-preview countdown (Community + temporary Pro Preview):

execute_command { command: "gitlens.plus.simulate.subscription", args: [{ "state": "Community", "featurePreviews": { "day": 0, "durationSeconds": 30 }, "dismissOnboarding": true }] }
# day: 1 → starts on day 2, day: 2 → day 3, day: 3 → preview expired

Stop simulation (mandatory teardown)

execute_command { command: "gitlens.plus.simulate.subscription", args: [{ "state": null }] }

Restores the prior subscription, feature previews, and any onboarding flags that were pre-dismissed via dismissOnboarding: true. Re-calling the start command also clears any prior simulation state.

Exercising AI features

GitLens AI features (Generate Commit Message, Explain *, Generate Changelog, Composer, Review Changes, Generate Search Query) cannot use real provider calls during automated inspection — they cost money, require keys, and produce non-deterministic outputs you can't assert against. The extension ships a deterministic AI simulator in DEBUG builds that you control via VS Code commands.

AI features are also Pro-gated, so two simulators must be enabled in order:

  1. Subscription simulator — see Exercising Pro-gated features above. Use state: "Paid", planId: "pro".
  2. AI simulator (gitlens.plus.simulate.ai) — replaces the AI provider with a stub that returns content the agent injects. Suppresses the first-run ToS modal and the AI All-Access promo notification automatically.

The AI simulator dispatches on a discriminated op arg: enable, disable, inject, clear, lastMessages. Calling without args opens a QuickPick.

Setup (one-time per session)

execute_command { command: "gitlens.plus.simulate.subscription", args: [{ "state": "Paid", "planId": "pro", "dismissOnboarding": true }] }
execute_command { command: "gitlens.plus.simulate.ai",           args: [{ "op": "enable" }] }

dismissOnboarding: true is documented in the Pro-gated features section — only needs to be set on one of the two commands. Both share the same snapshot/restore pattern.

Inject-then-trigger pattern

The agent authors the response content, pushes it onto the simulator's stash, then triggers the AI command. The next sendRequest for that action consumes the inject:

execute_command { command: "gitlens.plus.simulate.ai", args: [{
  "op": "inject",
  "action": "generate-commitMessage",
  "content": "<summary>Stable test summary</summary><body>Deterministic body content.</body>"
}] }
execute_command { command: "gitlens.ai.generateCommitMessage:scm" }
# read SCM input — assert it contains "Stable test summary"

inject payload: { op: "inject"; action?: AIActionType; content: string; sticky?: boolean }. Omit action to inject for the next call regardless of action. Set sticky: true to keep the same content for every call of that action.

Authoring response content

Action Format the content must satisfy
generate-commitMessage, generate-stashMessage, generate-changelog, generate-create-{cloudPatch|codeSuggestion|pullRequest} <summary>...</summary><body>...</body>
explain-changes (commit / branch / stash / wip / unpushed) Same summary/body XML
generate-searchQuery Plain string (the search query)
review-changes <overview>...</overview> followed by <area severity="..." files="..."><label>...</label><rationale>...</rationale><findings><finding severity="..." file="..." lines="..."><title>...</title><description>...</description></finding></findings></area> blocks
generate-commits (Composer) JSON tool-call output that conserves hunk indices from the prompt — see "Composer authoring" below

The schemas are enforced in packages/plus/ai/src/utils/results.utils.ts — read it once for the canonical tag set. The simulator returns a sensible default per action when no inject is queued, so smoke calls without explicit injects still produce predictable output.

Mode shortcuts (negative-path UX)

Without injecting per-call, you can globally force a failure mode by re-enabling with a different mode:

execute_command { command: "gitlens.plus.simulate.ai", args: [{ "op": "enable", "mode": "error" }] }    # provider error UX
execute_command { command: "gitlens.plus.simulate.ai", args: [{ "op": "enable", "mode": "cancel" }] }   # cancellation UX
execute_command { command: "gitlens.plus.simulate.ai", args: [{ "op": "enable", "mode": "slow" }] }     # progress-indicator UX
execute_command { command: "gitlens.plus.simulate.ai", args: [{ "op": "enable", "mode": "invalid" }] }  # composer 4-attempt validation-failure UX

Mode invalid only triggers retry behavior for generate-commits (the Composer) — other actions tolerate malformed content and just render it.

Composer authoring (reflection workflow)

The Composer's generate-commits validator demands every input hunk index appear exactly once in the response. The agent can't author a valid response without first seeing the hunks the prompt sent:

  1. Trigger Composer with no inject queued (default response will fail validation predictably)
  2. Read the messages the simulator received:
    execute_command { command: "gitlens.plus.simulate.ai", args: [{ "op": "lastMessages" }] }
    
    Returns AIChatMessage[]. Parse the user message's hunks JSON to learn the hunk index space.
  3. Author a valid {"commits":[{"message":"...","explanation":"...","hunks":[{"hunk":0},{"hunk":1},...]}, ...]} response covering every hunk index exactly once
  4. Inject for action generate-commits. The Composer's automatic 4-attempt retry loop will consume the inject on the next attempt.

Cleanup

Mandatory between scenarios — leftover injects from one test will leak into the next:

execute_command { command: "gitlens.plus.simulate.ai", args: [{ "op": "clear" }] }

Mandatory teardown (restores the prior gitlens.ai.model, gitlens.ai.enabled, confirm:ai:tos, and AI All-Access flags):

execute_command { command: "gitlens.plus.simulate.ai", args: [{ "op": "disable" }] }

{op: "enable"} always clears the stash first, so re-enabling between tests is also safe.

Build requirement (both simulators)

Both simulators are DEBUG-only (gitlens:debugging context). Standard dev launch via launch {} and pnpm run build:extension includes them. Production bundles strip them.

Related skills

  • /live-exercise — agent-driven iterative working rhythm for UI-bearing work (audit + fix loop), which uses this skill's tools as its primitive.
  • /live-perf — agent-driven performance measurement + improvement skill, also built on these primitives.
  • /live-pair — user-driven interactive pair-programming session; the user gives feedback, the agent edits/rebuilds/refreshes live.

Use /live-exercise, /live-perf, or /live-pair when touching UI; use this skill on its own for one-off inspection.

Install via CLI
npx skills add https://github.com/gitkraken/vscode-gitlens --skill live-inspect
Repository Details
star Stars 9,841
call_split Forks 1,736
navigation Branch main
article Path SKILL.md
More from Creator