agentleFS
PostHogKnown publisherPostHog/posthog-foss/.agents/skills/building-the-toolbar/SKILL.md

building-the-toolbar

Skill727 starsChanged yesterdayMITView on GitHub

Builds, watches, checks and verifies the PostHog toolbar in both of its forms, the floating toolbar that posthog-js loads on customer pages and the browser extension that shows the toolbar in a Chrome side panel or Firefox sidebar. Use when changing anything under frontend/src/toolbar/ (floating toolbar or extension), frontend/toolbar-config.mjs, or the toolbar build and guard scripts, or when asked to build, watch, load unpacked, or test the toolbar or the toolbar extension in a browser. Covers `pnpm build:toolbar-all` and `pnpm watch:toolbar-all`, the check-toolbar-size, check-toolbar-graph and check-toolbar-csp-eval guards, why both forms must be built and kept in sync, and how to check each one in a real browser.

We split this skill into the rules it gives an agent

10 rules. About 0 are common, 5 are distinctive, and 5 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈5

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 5

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Put logic in shared/. A form folder configures shared code for its form: it mounts logics, binds them to its UI, and renders shared …

    line 33
  2. Very specific:

    When a component is the same in both forms, it lives in shared/ and takes its data and callbacks as props.

    line 34
  3. Very specific:

    Keep each kea logic's path([...]) as it is when you move its file.

    line 36
  4. Distinctive:

    Do not add an extension-only branch to shared code when a fix that serves both forms exists.

    line 46
  5. Distinctive:

    When a branch is unavoidable, keep it in one place and say why in a comment.

    line 47
  6. Very specific:

    When check-toolbar-graph fails, cut the new import rather than add it to bin/toolbar-graph-baseline.json.

    line 78
  7. Distinctive:

    After a rebuild, reload the extension, then close and open the panel.

    line 96
  8. Distinctive:

    Check both browsers when you change the extension, and walk the README's manual checks for the tools you touched.

    line 97
  9. Very specific:

    Floating toolbar: Start the dev stack (see /run-posthog), open the Toolbar page at http://localhost:8010/toolbar, add a test site that …

    line 98
  10. Distinctive:

    Check the forms you didn't mean to change as well.

    line 101

≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Building the toolbar

One source tree, frontend/src/toolbar/, ships in two forms. Both builds live in frontend/toolbar-config.mjs and share its toolbar module plugin, which swaps app modules for the toolbar's shims and keeps denylisted packages out.

FormEntryOutput (under frontend/dist/)Runs in
Floating toolbarsrc/toolbar/floating/index.tsxtoolbar.js loader, toolbar/ app chunksCustomer pages, loaded by posthog-js
Browser extensionsrc/toolbar/extension/panel.tsx, content.tsxtoolbar-extension/<browser>/, one zip per browserChrome side panel, Firefox sidebar (Manifest V3)

Where code goes

FolderHolds
toolbar/shared/The kea logics, the API and auth helpers, the shims, utils, and every component both forms render
toolbar/floating/The floating toolbar: its entry and loader, the bar, and the menus that bind shared logic to its UI
toolbar/extension/The extension: the panel, the content script, the page agent, and the panel's own tool components

Inside floating/ and extension/, only the entry points the build names sit at the top. Everything else goes in a folder:

FolderHolds
components/<feature>/React components, their stories and tests. The feature folders use the names shared/ uses, such as flags
logics/The form's kea logics and the pure helpers they test. The extension splits them into panel/ and page/ by where they run
messaging/Extension only: the port, page agent and form sync protocol between the panel, the content script and the page
browser/Extension only: the wrappers around the extension APIs
  • Put logic in shared/. A form folder configures shared code for its form: it mounts logics, binds them to its UI, and renders shared components with its own data.
  • When a component is the same in both forms, it lives in shared/ and takes its data and callbacks as props. WebExperimentVariant is the pattern: floating/components/experiments/ExperimentsTabEditorProvider.tsx and extension/components/experiments/ExtensionExperimentEditorProvider.tsx each bind it to their own state.
  • shared/ never imports from floating/ or extension/, and the two forms never import each other. toolbar/toolbarLayout.test.ts fails on such an import.
  • Keep each kea logic's path([...]) as it is when you move its file. The path is the logic's identity and storage key, not its location.

Build both, every time

The extension's content script, extension/content.tsx, runs the shared kea logics and components on the page, such as toolbarConfigLogic, elementsLogic and ElementOverlays. The panel imports shared modules too, and both go through the same module plugin. So any change under toolbar/shared/, or in toolbar-config.mjs, can break either form, even when you only meant to touch one.

  • Build and check both forms for every toolbar change.
  • Fix shared toolbar code in the general way. Do not add an extension-only branch to shared code when a fix that serves both forms exists. When a branch is unavoidable, keep it in one place and say why in a comment.

From frontend/:

pnpm build:toolbar-all    # production floating toolbar, then the extension
pnpm watch:toolbar-all    # rebuild both on change

To build one form only:

FormBuildWatch
Floating toolbarpnpm build:toolbar-floatingpnpm watch:toolbar-floating
Browser extensionpnpm build:toolbar-extensionpnpm watch:toolbar-extension

The dev stack (hogli start) already runs pnpm watch:toolbar-floating through start-vite. While it runs, start pnpm watch:toolbar-extension alone, so two watchers don't write the same files.

Run the guards

The guards read toolbar-esbuild-meta.json and dist/toolbar*, which the floating toolbar build writes. CI runs them on a production build, so run them after pnpm build:toolbar-all, not after a watch build:

pnpm check-toolbar-size      # loader size, eager-set budget, every file under the CloudFront gzip limit
pnpm check-toolbar-graph     # no new toolbar-to-app import edges, no denylisted packages
pnpm check-toolbar-csp-eval  # no eval, new Function or string timers in shipped code

Each script's header comment explains its limits. When check-toolbar-graph fails, cut the new import rather than add it to bin/toolbar-graph-baseline.json.

These guards cover the floating toolbar bundle only. The extension has no guard script, but Manifest V3 refuses eval and remote scripts, so a break there shows when you load the extension.

Also run the toolbar tests and the logic types:

pnpm exec jest src/toolbar
pnpm typegen:write   # then check that git diff shows no drift in logic types

Check it in a real browser

Tests and builds don't show a broken overlay, a missing style, or a CSP refusal. Load the build.

  • Extension. Load dist/toolbar-extension/chrome unpacked in Chrome and dist/toolbar-extension/firefox as a temporary add-on in Firefox. The steps are in the extension README; read it before you load the extension the first time. After a rebuild, reload the extension, then close and open the panel. Check both browsers when you change the extension, and walk the README's manual checks for the tools you touched.
  • Floating toolbar. Start the dev stack (see /run-posthog), open the Toolbar page at http://localhost:8010/toolbar, add a test site that loads posthog-js to the authorized URLs, and launch the toolbar on it. Toolbar.stories.tsx renders the floating toolbar in Storybook when you only need to look at a component.

Check the forms you didn't mean to change as well. A change to a shared logic shows up in both.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.