> capture, openExternal, navigate), the data hierarchy (saved insights first, typed query nodes second, inline HogQL last), per-insight-type result shapes, date-range wiring, and event capture from a canvas. Use whenever a canvas shows metrics, charts, tables, or any PostHog data, or needs to send analytics events.
npx skills add https://github.com/PostHog/posthog --skill querying-canvas-data
The global ph object (injected by the host — never imported, never initialized) is the only way
a canvas talks to PostHog. Credentials stay in the host; fetch(), posthog-js, and hand-rolled
clients fail in the sandbox.
create/save an insight that computes the metric with an insight query type (TrendsQuery,
FunnelsQuery, RetentionQuery, PathsQuery, or the web-analytics kinds WebOverviewQuery /
WebStatsTableQuery — not raw SQL). Confirm its numbers, note the short_id, and render it with
await ph.loadInsight(shortId, { dateRange }). These are proven queries — numbers match the
PostHog UI exactly (sessionization, unique users, breakdowns, bounce rate). Never fabricate a
query or guess event/property names; discover and save them via MCP first.
ph.query({ kind: "TrendsQuery", series: [...], dateRange: {...} })when saving an insight genuinely doesn't fit.
ph.query("SELECT …"), only when no insight kind can expressthe metric; you then own the SQL and its date window.
For web-analytics boards specifically, use the web-analytics query kinds — raw HogQL subtly gets
bounce rate, sessionization, channel attribution, and unique-visitor counts wrong.
Whatever tier you use, declare it in the project's capabilities before publishing: every
ph.loadInsight short id in capabilities.posthog.insights, every ph.capture event name in
captureEvents, and inlineQueries: true for any ph.query use. The host rejects undeclared
calls at runtime, and validation fails on undeclared literals.
ph.loadInsight or a typed node): resultsis an array of series objects, not rows. Each series has data: number[] (per interval),
days: string[] (ISO), labels: string[], count (sum), aggregated_value (single-value
total), label, and optional compare_label: "current" | "previous". A KPI total is
results[0].count (or .aggregated_value); a line chart plots results[0].data over
results[0].days. With a compare period, find the prior series by compare_label === "previous"
— never by index. columns is empty here.
{ columns: string[], results: rows[][] } — each row an array of cell values incolumns order.
Load data in useEffect with useState, show a loading state, and handle empty/error. Aggregate
in the query; never fetch raw event dumps.
dateRange:ph.loadInsight(shortId, { dateRange: { date_from: win.start.toISOString(), date_to: win.end.toISOString() } })
— the saved insight re-scopes to the window with no time SQL. Typed nodes take the same
dateRange. Re-run every query when the window changes.
dateRange (its window lives inside the SQL) — a reason toprefer insight query types.
now() or a hardcoded INTERVAL. Compute unix bounds(Math.floor(win.start.getTime() / 1000)) and write half-open
timestamp >= toDateTime(fromUnix) AND timestamp < toDateTime(toUnix). Prior period = the
equal-length window immediately before; bucket with toStartOfDay/toStartOfHour.
ph.capture(event, properties?, distinctId?) — analytics events for interactions(fire-and-forget). Session replay, $session_id, and person attribution are handled by the
host automatically; never roll your own capture.
ph.openExternal(url) — opens https://posthog.com / *.posthog.com URLs only, and only froma user interaction (opens outside focus are ignored). Don't link elsewhere.
ph.navigate.toTask(id) / .toNewTask() / .toCanvas(id) / .toNewCanvas() — in-appnavigation within the canvas's own channel.
Automate YouTube tasks via Rube MCP (Composio): upload videos, manage playlists, search content, get analytics, and handle comments. Always search tools first for current schemas.
Create and audit truthful, accessible, publication-ready scientific figures with Matplotlib, Seaborn, or Plotly. Use for figure design, multi-panel layouts, uncertainty and missing-data displays, color/contrast review, image metadata validation, and journal export planning.
This skill should be used when comparing two videos to analyze compression results or quality differences. Generates interactive HTML reports with quality metrics (PSNR, SSIM) and frame-by-frame visual comparisons. Triggers when users mention "compare videos", "video quality", "compression analysis", "before/after compression", or request quality assessment of compressed videos.
Python bridge to ImageJ2/Fiji for macros, plugins (Bio-Formats, TrackMate, Analyze Particles), NumPy↔ImagePlus/ImgLib2 exchange, and ImageJ Ops. Automates Fiji headlessly from Python. Use scikit-image for pure Python without Fiji plugins; napari for visualization.
Create 3D scenes, interactive experiences, and visual effects using Three.js. Use when user requests 3D graphics, WebGL experiences, 3D visualizations, animations, or interactive 3D elements.
Generate publication-quality PNG chart images from data, supporting line, bar, area, candlestick, pie, and heatmap charts. Triggers when the user asks to visualize data, create a graph, plot a time series, or generate a chart for a report, alert, or dashboard. Runs as a lightweight, headless Node.js process without a browser.
Performs deep Root Cause Analysis (RCA) on NVIDIA TAO Visual ChangeNet classification experiments with image-evidence-driven investigation. Use when analyzing ChangeNet model failures, investigating poor recall / FAR / PASS-NO_PASS metrics, auditing visual inspection pipeline quality, or running an RCA report for an AOI defect-detection model. Trigger phrases include "RCA on my ChangeNet model", "why is my AOI model failing", "audit ChangeNet predictions", "investigate FAR regressions", "root cause analysis on visual-changenet".
Build 3D web apps with Three.js (WebGL/WebGPU). Use for 3D scenes, animations, custom shaders, PBR materials, VR/XR experiences, games, data visualizations, product configurators.
Take posthog/querying-canvas-data from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.