Skip to content

Commit 9ccc8fc

Browse files
chore: sync skills (agent-skills-v0.315.0, context-mill@v1.26.1)
1 parent 144cc7b commit 9ccc8fc

39 files changed

Lines changed: 2611 additions & 4742 deletions

File tree

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "posthog",
33
"description": "Access PostHog analytics, feature flags, experiments, error tracking, and insights directly from your AI coding tool. Optionally capture Claude Code sessions to PostHog LLM Analytics.",
4-
"version": "1.1.45",
4+
"version": "1.1.46",
55
"author": {
66
"name": "PostHog",
77
"email": "hey@posthog.com",

.codex-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "posthog",
3-
"version": "1.0.43",
3+
"version": "1.0.44",
44
"description": "Access PostHog analytics, feature flags, experiments, error tracking, and insights directly from Codex",
55
"author": {
66
"name": "PostHog",

.cursor-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "posthog",
33
"displayName": "PostHog",
4-
"version": "1.1.39",
4+
"version": "1.1.40",
55
"description": "Access PostHog analytics, feature flags, experiments, error tracking, and insights directly from Cursor",
66
"author": {
77
"name": "PostHog",

gemini-extension.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "posthog",
3-
"version": "1.0.41",
3+
"version": "1.0.42",
44
"description": "Access PostHog analytics, feature flags, experiments, error tracking, and insights directly from Gemini CLI",
55
"mcpServers": {
66
"posthog": {

skills/.sync-manifest

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,13 @@ signals-scout-health-checks
8989
signals-scout-inbox-validation
9090
signals-scout-insight-alerts
9191
signals-scout-logs
92+
signals-scout-mcp-tool-calls
9293
signals-scout-observability-gaps
9394
signals-scout-product-analytics
9495
signals-scout-replay-vision
9596
signals-scout-revenue-analytics
9697
signals-scout-session-replay
98+
signals-scout-skills-store
9799
signals-scout-surveys
98100
signals-scout-web-analytics
99101
skills-store

skills/authoring-scouts/SKILL.md

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -166,29 +166,58 @@ config on the default every-24-hours schedule on its next tick (up to ~30 min).
166166

167167
## Test loop
168168

169-
You can't force a synchronous run as a user — scouts fire on their schedule. The standard
170-
loop is **emit + inspect**: ship the scout live, let it emit, and calibrate against what
171-
actually lands.
172-
173-
1. Ship the scout (the default `emit=true`) with a short `run_interval_minutes` so it fires
174-
soon — set it at creation via
175-
`posthog:signals-scout-config-create {"skill_name": ..., "run_interval_minutes": 30}`
176-
right after `skill-create`, rather than waiting for the coordinator to
177-
auto-register the default every-24-hours schedule.
178-
2. After a tick, read what it did: `posthog:inbox-reports-list` (the findings it actually
179-
emitted), `posthog:signals-scout-runs-list` (run summaries), `-runs-retrieve` (full
169+
**Dogfood the scout yourself before you ever spend a real run.** You — the agent authoring
170+
the scout — have the same PostHog MCP tools a scout uses at runtime (`execute-sql`,
171+
`read-data-schema`, the per-product list tools, `signals-scout-project-profile-get`). The
172+
cheapest, fastest iteration doesn't touch a scout run at all: walk the scout's own logic
173+
against the live project by hand. Confirm the watched event/entity exists and has the shape
174+
you assumed, run the **discriminator** to check it actually separates signal from noise on
175+
_this_ project's data, and run each **explore pattern**'s queries to see what they surface.
176+
This loop is free and instant — refine the body against what you find, re-run the queries,
177+
repeat, until the scout's logic holds up on real data. This is where the real iteration
178+
happens.
179+
180+
Only once you're happy with the body do you spend an actual run.
181+
`posthog:signals-scout-run-now {"id": <config_id>}` dispatches one run of the scout
182+
immediately, regardless of its schedule (find the `id` via `-config-list`). This is the
183+
**initial real run** — the scout executing end-to-end in the harness, writing scratchpad
184+
memory and (with the default `emit=true`) emitting to the inbox. The run is **asynchronous**:
185+
the call returns a workflow id right away, so poll `-runs-list` / `-runs-retrieve` for the
186+
result. A few things to know:
187+
188+
- A **disabled** scout can still be run this way — you can test it before ever enabling it.
189+
- A manual run does **not** change the scout's schedule or `last_run_at`.
190+
- It inherits every guard the scheduled path has: 403 if scouts aren't enabled for the
191+
project, 429 if the project is over its Signals credits quota or daily run budget, 409 if a
192+
run for this scout is already in progress.
193+
- It draws from the **same daily run budget** as scheduled runs — and a dry-run
194+
(`emit=false`) still consumes a run. There's no free test run: every `-run-now` spends the
195+
project's daily scout-run allowance, so firing the same scout repeatedly in a short window
196+
burns through the budget (and can leave the project's scheduled scouts unable to run that
197+
day). **Don't use `-run-now` as your iteration loop** — it's slow (async, one run per call)
198+
and metered. Dogfood the queries by hand to get the body right; reserve `-run-now` for the
199+
initial real run and the occasional re-check after a genuinely meaningful change.
200+
201+
The standard loop is **dogfood → run once ready → inspect**:
202+
203+
1. Dogfood the discriminator + explore patterns yourself against the live project (above).
204+
Refine the body until the logic holds on real data — this is the cheap, iterable part.
205+
2. Author the scout and register its config (`-config-create`, the default `emit=true`), then
206+
spend one `-run-now` to watch the whole scout execute end-to-end. Leave
207+
`run_interval_minutes` at a sustainable value — you no longer need a short interval to
208+
force an early run.
209+
3. After the run finishes, read what it did: `posthog:inbox-reports-list` (the findings it
210+
actually emitted), `posthog:signals-scout-runs-list` (run summaries), `-runs-retrieve` (full
180211
reasoning for one run), and `-scratchpad-search` (the durable memory it wrote).
181-
3. Refine the body — tighten the discriminator, add disqualifiers for whatever it
182-
false-positived on, fix the emit calibration.
183-
4. Once it's landing the right findings, restore the interval to something sustainable
184-
(the 3h default or longer).
212+
4. If it needs work, go back to dogfooding the queries by hand for the iteration — only spend
213+
another `-run-now` once you've batched a meaningful change worth a fresh end-to-end run.
185214

186215
**Want to be extra careful?** Set `emit=false` to dry-run first — create the config with
187-
`emit=false` via `-config-create` so the scout never has a live first run; it runs and logs
188-
what it _would_ have emitted (visible via `-runs-list` / `-runs-retrieve`) without writing to
189-
the inbox. Inspect, refine, then flip `emit=true`. Worth it for a scout you expect to be
190-
chatty, expensive, or high-stakes; otherwise just emitting and watching the inbox is the
191-
faster path to a calibrated scout.
216+
`emit=false` via `-config-create`, then trigger it with `-run-now`: it runs and logs what it
217+
_would_ have emitted (visible via `-runs-list` / `-runs-retrieve`) without writing to the
218+
inbox. Inspect, refine, then flip `emit=true` and run it again. Worth it for a scout you
219+
expect to be chatty, expensive, or high-stakes; otherwise just emitting and watching the
220+
inbox is the faster path to a calibrated scout.
192221

193222
Repo contributors get a faster loop — `hogli sync:skill` and the harness's local run path;
194223
see [`references/lifecycle-and-testing.md`](references/lifecycle-and-testing.md).

skills/authoring-scouts/references/lifecycle-and-testing.md

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -98,27 +98,46 @@ the standalone skills repo) automatically.
9898

9999
## Testing
100100

101-
You can't trigger a synchronous run as a user — scouts fire on their schedule. The standard
102-
loop is **emit + inspect**: ship the scout live (`emit=true` is the default), let it emit,
103-
and calibrate against what actually lands.
104-
105-
1. Ship with the default `emit=true` and a short `run_interval_minutes` (e.g. 30) so it
106-
fires soon — set both at creation via `posthog:signals-scout-config-create`.
107-
2. After a tick, inspect:
101+
**Dogfood the scout yourself first — before spending any real run.** The authoring agent has
102+
the same PostHog MCP tools a scout uses at runtime (`execute-sql`, `read-data-schema`, the
103+
per-product list tools, `signals-scout-project-profile-get`), so the cheapest iteration is to
104+
walk the scout's own logic against the live project by hand: confirm the watched entity
105+
exists and has the assumed shape, run the **discriminator** to check it separates signal from
106+
noise on this project's data, and run each **explore pattern**'s queries. Free and instant —
107+
refine the body, re-run the queries, repeat, until the logic holds on real data.
108+
109+
Only once you're happy do you spend a real run. `posthog:signals-scout-run-now {"id":
110+
<config_id>}` dispatches one run of the scout immediately, regardless of its schedule (get the
111+
`id` from `-config-list`) — the **initial real run**, the scout executing end-to-end in the
112+
harness. The run is **asynchronous** — the call returns a workflow id right away; poll
113+
`-runs-list` / `-runs-retrieve` for the result. A disabled scout can still be run this way
114+
(test before enabling), and a manual run doesn't touch the schedule or `last_run_at`. It
115+
inherits the scheduled path's guards (403 not enabled, 429 over quota / daily run budget, 409
116+
a run already in progress) and draws from the **same daily run budget** as scheduled runs — a
117+
dry-run (`emit=false`) counts too. There's no free test run, and it's slow (async, one run per
118+
call): firing the same scout repeatedly in a short window burns the project's daily allowance
119+
(and can starve its scheduled scouts). **Don't iterate via `-run-now`** — dogfood the queries
120+
by hand to get the body right, and reserve `-run-now` for the initial real run and the odd
121+
re-check after a genuinely meaningful change. The loop is **dogfood → run once ready →
122+
inspect**:
123+
124+
1. Dogfood the discriminator + explore patterns yourself against the live project (above),
125+
refining the body until the logic holds — the cheap, iterable part.
126+
2. Author the scout and register its config (`-config-create`, default `emit=true`), leaving
127+
`run_interval_minutes` at a sustainable value — no short-interval trick needed. Then spend
128+
one `-run-now` to watch the whole scout execute end-to-end, and inspect once it finishes:
108129
- `posthog:inbox-reports-list` — the findings it actually emitted.
109130
- `posthog:signals-scout-runs-list` — run summaries.
110131
- `posthog:signals-scout-runs-retrieve` — the full reasoning for one run.
111132
- `posthog:signals-scout-scratchpad-search` — the durable memory it wrote.
112-
3. Refine the body for whatever it false-positived or missed — tighten the discriminator,
113-
add disqualifiers, fix emit calibration. Re-edit via `skill-update`.
114-
4. Once it's landing the right findings, `config-update` to restore a sustainable interval
115-
(the 3h default or slower).
133+
3. If it needs work, go back to dogfooding the queries by hand for the iteration, re-edit via
134+
`skill-update`, and spend another `-run-now` only once you've batched a meaningful change.
116135

117136
**Extra-careful variant — dry-run first.** For a scout you expect to be chatty, expensive,
118137
or high-stakes, set `emit=false` so it runs and logs what it _would_ have emitted (visible in
119-
`-runs-list` / `-runs-retrieve`) without writing to the inbox. Inspect, refine, then
120-
`config-update` to `emit=true`. For most scouts, emitting straight away and watching the
121-
inbox is the faster calibration.
138+
`-runs-list` / `-runs-retrieve`) without writing to the inbox. Trigger it with `-run-now`,
139+
inspect, refine, then `config-update` to `emit=true`. For most scouts, emitting straight away
140+
and watching the inbox is the faster calibration.
122141

123142
Repo contributors additionally get `hogli sync:skill` to run the scout against the local
124143
harness for a tighter loop before merging.

skills/configuring-experiment-analytics/SKILL.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,16 +81,18 @@ compare on what each metric measures (its `query`), never on its title.
8181
(mean / funnel / ratio / retention) before searching — see Step 2 to confirm the event exists via
8282
`read-data-schema`. You can only recognize a duplicate once you know the concrete event/action,
8383
so this check runs _after_ you've pinned down the event, not before.
84-
2. **List the library and compare each candidate's `query`.** Call `experiment-saved-metrics-list`
85-
and inspect every result's **`query`** field (not just `name`/`description`). A saved metric is a
86-
reuse match when its `query` measures the **same event or action with the same `metric_type`**
87-
(and compatible `math`) as the metric you'd otherwise build — even if its name is
88-
different.
89-
- **Match locally, not via `search`.** `search` matches only `name` / `description` / tags —
90-
never the underlying event or action — so it cannot find a definition match, and an empty
91-
result means nothing here. Page through the full library with `limit`/`offset` and compare each
92-
row's `query` yourself. (Use `search` only when the user names a specific saved metric to
93-
attach — that's name resolution, not a definition match.)
84+
2. **Search by the event, then compare each candidate's `query`.** Call `experiment-saved-metrics-list`
85+
with `?event=<the event you're measuring>` to find metrics that reference it — matched directly (an
86+
`EventsNode`) **or** via the step events of any action a metric references, so action-based metrics are
87+
found by the event their action fires on. Then for each returned row, inspect its **`query`** (not the
88+
`name`/`description`): a saved metric is a reuse match when its `query` measures the **same event or
89+
action with the same `metric_type`** (and compatible `math`) as the metric you'd otherwise build, even
90+
if its name is different.
91+
- **Match on the event, not the action's name.** An action-based metric is discoverable by the event
92+
the action fires on — pass that event, not the action's label.
93+
- **Do not use `search` for this.** `search` matches only the metric's own `name` / `description` / tags —
94+
never the underlying event or action — so it cannot find a definition match. Use `search` only when the
95+
user names a specific saved metric to attach (name resolution, not a definition match).
9496
3. **If a saved metric matches the definition** — confirm the match with the user by name/description,
9597
then attach it instead of building a new one:
9698
- Call `experiment-get` to read the experiment's current `saved_metrics`.

skills/designing-email-templates/SKILL.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ Marketing emails must include an unsubscribe link — render it with the built-i
3131

3232
(`{{ unsubscribe_url_one_click }}` is also available for one-click list-unsubscribe flows.)
3333

34+
## Click tracking and opt-out
35+
36+
Every link is automatically rewritten through a click-tracking redirect. This breaks mobile universal links / app deeplinks, which only resolve when the href stays on their own domain. To keep a link untracked, mark its anchor (use an `html` block) with `clicktracking="off"` or `data-ph-no-track`:
37+
38+
```html
39+
<a href="https://app.example.com/deeplink" data-ph-no-track>Open in app</a>
40+
```
41+
42+
The marker must be on the `<a>` tag itself, not a child element. Opted-out links get no click metrics.
43+
3444
## Creating a template
3545

3646
Call `workflows-create-email-template` with:

skills/exploring-apm-traces/SKILL.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,13 @@ posthog:apm-trace-get
4343
}
4444
```
4545

46-
The response is `{ results: [span, span, …] }` — a flat list of every span in the trace.
46+
The response is `{ results: [span, span, …], _posthogUrl: "…" }` — a flat list of every span in the trace.
4747
The list can be very large for fan-out request flows; when it exceeds the inline limit, Claude Code auto-persists it to a file.
4848

4949
From the result you get:
5050

5151
- Every span with `name`, `service_name`, `kind`, `status_code`, `parent_span_id`, `duration_nano`, `is_root_span`
52-
- The `_posthogUrl`**always include this in your response** so the user can click through to the UI
52+
- The `_posthogUrl`a deep link to this trace in the tracing UI; **always include this in your response** so the user can click through
5353

5454
### Step 2 — Parse large results with scripts
5555

@@ -148,9 +148,11 @@ Each span carries an `attributes` map (span-level OTel attributes like `http.met
148148

149149
## Constructing UI links
150150

151-
`apm-trace-get` and `query-apm-spans` return `_posthogUrl`**always surface this to the user** so they can verify in the PostHog UI.
151+
`apm-trace-get` returns a `_posthogUrl` deep link that opens the trace in the tracing UI**always surface this to the user** so they can verify in the PostHog UI.
152152

153-
When presenting findings, include the relevant PostHog URL.
153+
`query-apm-spans` does not return `_posthogUrl`.
154+
To link a trace found via the query tool, feed its `trace_id` to `apm-trace-get` and surface the `_posthogUrl` from that response.
155+
Never hand-construct PostHog URLs.
154156

155157
## Finding traces
156158

@@ -237,7 +239,7 @@ results (array of span dicts)
237239
## Tips
238240

239241
- Always set `dateRange` on `query-apm-spans` — queries without a time range are slow. Default is `-1h`; widen only when needed.
240-
- Always include the `_posthogUrl` in your response so the user can click through.
242+
- Always include the `_posthogUrl` from `apm-trace-get` in your response so the user can click through to the trace.
241243
- Span-level attributes **are** in the `apm-trace-get` / `query-apm-spans` payload (each span's `attributes` map). Resource attributes are not — use `apm-attributes-list` (type `resource`) and `apm-attribute-values-list` for those.
242244
- `is_root_span` is the cheap way to find the trace entry — don't string-match `00000000…`.
243245
- For aggregates (p95 by operation, slowest children of a span), use `apm-spans-aggregate` for a flat view or `apm-spans-tree` for parent→child edges — don't reach for SQL.

0 commit comments

Comments
 (0)