/managing-path-cleaning-rules
Inspects URL paths and proposes, tests, orders, and applies project-level path cleaning rules so dynamic segments (numeric IDs, UUIDs, slugs, dates) collapse into readable aliases. Use when the user says "clean the paths", "normalize URLs", "group similar pages", "too many
Claude Code
$ npx -y skills add PostHog/ai-plugin --skill managing-path-cleaning-rules --agent claude-code
Codex
$ npx skills add PostHog/ai-plugin --skill managing-path-cleaning-rules -a codex -y
Cursor
$ npx skills add PostHog/ai-plugin --skill managing-path-cleaning-rules -a cursor -y
Gemini CLI
$ npx skills add PostHog/ai-plugin --skill managing-path-cleaning-rules -a gemini-cli -y
GitHub Copilot
$ npx skills add PostHog/ai-plugin --skill managing-path-cleaning-rules -a github-copilot -y
OpenCode
$ npx skills add PostHog/ai-plugin --skill managing-path-cleaning-rules -a opencode -y
Windsurf
$ npx skills add PostHog/ai-plugin --skill managing-path-cleaning-rules -a windsurf -y
SKILL.md
managing-path-cleaning-rules.SKILL.mdname: managing-path-cleaning-rules
description: 'Inspects URL paths and proposes, tests, orders, and applies project-level path cleaning rules so dynamic segments (numeric IDs, UUIDs, slugs, dates) collapse into readable aliases. Use when the user says "clean the paths", "normalize URLs", "group similar pages", "too many distinct paths", "/users/123 and /users/456 are the same page", "set up path cleaning", or asks why a Web analytics or Paths breakdown is fragmented across thousands of nearly-identical URLs. Covers regex syntax (re2), alias placeholder convention, rule ordering, the test workflow, and applying rules via the path-cleaning-rules-update MCP tool.'
Managing path cleaning rules
Path cleaning rules normalize `$pathname` and `$entry_pathname` so that pages sharing the same template (`/users/123/profile`, `/users/456/profile`, …) collapse into one row (`/users/<id>/profile`) in Web analytics tiles, Paths insights, and any HogQL query that calls `apply_path_cleaning`. They are the right answer when a breakdown is fragmented across thousands of near-identical URLs.
This skill teaches you how to:
- recognize when path cleaning is the right tool
- inspect real paths to find what needs cleaning
- write `regex` + `alias` rules in re2 syntax with the project's placeholder
convention
- test rules before saving them
- order rules so specific patterns aren't swallowed by generic ones
- apply the rules via MCP
Data model
`Team.path_cleaning_filters` is a JSON list of `PathCleaningFilter` objects:
{
"regex": "/users/\\d+/profile",
"alias": "/users/<id>/profile",
"order": 0
}- **`regex`** — a [re2](https://github.com/google/re2/wiki/Syntax) pattern. No
need to escape `/`. Anchor with `^` / `$` when you mean it.
- **`alias`** — the replacement. Default to angle-bracket placeholders
(`<id>`, `<slug>`, `<uuid>`, `<date>`) so the cleaned path stays human-readable. Backreferences to the regex's capture groups also work, but they cost more to evaluate — see the backreference pitfall below before reaching for one.
- **`order`** — integer. Rules apply **sequentially** in `order` ascending,
each rule's output feeds the next.
Application is `replaceRegexpAll(pathname, regex, alias)` per rule, chained.
Workflow
1. Confirm path cleaning is the right move
Ask yourself: is the user complaining about cardinality (too many distinct paths in a chart), or do they want a per-URL drill-down? Path cleaning is for the former. If they want per-URL data, suggest a property filter on `$pathname` instead.
2. Inspect the real paths
Don't guess at patterns — query them. With the `execute-sql` MCP tool:
SELECT properties.$pathname AS path, count() AS views
FROM events
WHERE event = '$pageview'
AND timestamp > now() - INTERVAL 7 DAY
GROUP BY path
ORDER BY views DESC
LIMIT 200
Scan the result for:
- numeric IDs: `/users/123`, `/orders/4242`
- UUIDs: `/sessions/8f3c1a3b-…`
- slugs: `/posts/why-i-love-posthog`
- dates: `/archive/2024-09-12`
- locales: `/en-US/`, `/fr-FR/`
- pagination: `?page=3`, `/page/3/`
3. Draft regex + alias
| Pattern | Example match | `regex` | `alias` | | ------------------- | ---------------------- | ---------------------------- | ---------------------- | | Numeric segment | `/users/123/profile` | `/users/\d+/profile` | `/users/<id>/profile` | | UUID v4 | `/sessions/8f3c1a3b-…` | `/sessions/[0-9a-f-]{36}` | `/sessions/<uuid>` | | Slug | `/posts/why-posthog` | `/posts/[a-z0-9-]+$` | `/posts/<slug>` | | ISO date | `/archive/2024-09-12` | `/archive/\d{4}-\d{2}-\d{2}` | `/archive/<date>` | | Locale prefix | `/en-US/about` | `^/[a-z]{2}-[A-Z]{2}/` | `/<locale>/` | | Trailing query/page | `/blog?page=3` | `\?page=\d+$` | (empty alias drops it) |
Anchoring rules of thumb:
- start the regex with `^` only when the segment must be at the beginning of
the path
- end with `$` to keep a generic rule (e.g. `\d+$`) from matching mid-path
segments
4. Test before saving
Three options, pick one:
- **Settings page tester**: `/settings/project#path_cleaning` has a built-in
"test path" input that replays the full ordered chain.
- **Project HogQL** (via `execute-sql`):
SELECT replaceRegexpAll('/users/42/profile', '/users/\d+/profile', '/users/<id>/profile')Chain `replaceRegexpAll` calls in the same order the rules will run if you want to verify multi-rule interaction.
- **Built-in AI helper**: there is already an `AiRegexHelper` modal accessible
from the rule editor (`Help me with Regex` button) that turns natural language into a regex. Suggest it to the user when they say "I don't know regex" — but always validate the output against real paths via the tester.
5. Order rules from most-specific to most-general
Sequential application means a generic rule placed first will swallow everything that should have hit a specific rule.
order=0 /users/me/profile → /users/me/profile (specific, runs first)
order=1 /users/\d+/profile → /users/<id>/profile
order=2 /users/[a-z0-9-]+ → /users/<slug> (catch-all, runs last)
If `/users/[a-z0-9-]+` ran first it would also match `/users/me/profile` and make the more specific rule unreachable.
6. Apply via MCP
Prefer the `path-cleaning-rules-update` tool. It reads the current rules, applies granular operations (`append`, `insert`, `replace`, `remove`, `reorder`), auto-numbers `order`, and — unless you pass `confirm: true` — returns a **preview** of the resulting rules without saving. Pass `sample_paths` to see how the resulting set rewrites real paths.
First call it without `confirm` to get the preview, surface that to the user, then re-send the same call with `"confirm": true` to save:
{
"oRead more
name: managing-path-cleaning-rules description: 'Inspects URL paths and proposes, tests, orders, and applies project-level path cleaning rules so dynamic segments (numeric IDs, UUIDs, slugs, dates) collapse into readable aliases. Use when the user says "clean the paths", "normalize URLs", "group similar pages", "too many distinct paths", "/users/123 and /users/456 are the same page", "set up path cleaning", or asks why a Web analytics or Paths breakdown is fragmented across thousands of nearly-identical URLs. Covers regex syntax (re2), alias placeholder convention, rule ordering, the test workflow, and applying rules via the path-cleaning-rules-update MCP tool.'
Managing path cleaning rules
Path cleaning rules normalize `$pathname` and `$entry_pathname` so that pages sharing the same template (`/users/123/profile`, `/users/456/profile`, …) collapse into one row (`/users/<id>/profile`) in Web analytics tiles, Paths insights, and any HogQL query that calls `apply_path_cleaning`. They are the right answer when a breakdown is fragmented across thousands of near-identical URLs.
This skill teaches you how to:
- recognize when path cleaning is the right tool
- inspect real paths to find what needs cleaning
- write `regex` + `alias` rules in re2 syntax with the project's placeholder
convention
- test rules before saving them
- order rules so specific patterns aren't swallowed by generic ones
- apply the rules via MCP
Data model
`Team.path_cleaning_filters` is a JSON list of `PathCleaningFilter` objects:
{
"regex": "/users/\\d+/profile",
"alias": "/users/<id>/profile",
"order": 0
}- **`regex`** — a [re2](https://github.com/google/re2/wiki/Syntax) pattern. No
need to escape `/`. Anchor with `^` / `$` when you mean it.
- **`alias`** — the replacement. Default to angle-bracket placeholders
(`<id>`, `<slug>`, `<uuid>`, `<date>`) so the cleaned path stays human-readable. Backreferences to the regex's capture groups also work, but they cost more to evaluate — see the backreference pitfall below before reaching for one.
- **`order`** — integer. Rules apply **sequentially** in `order` ascending,
each rule's output feeds the next.
Application is `replaceRegexpAll(pathname, regex, alias)` per rule, chained.
Workflow
1. Confirm path cleaning is the right move
Ask yourself: is the user complaining about cardinality (too many distinct paths in a chart), or do they want a per-URL drill-down? Path cleaning is for the former. If they want per-URL data, suggest a property filter on `$pathname` instead.
2. Inspect the real paths
Don't guess at patterns — query them. With the `execute-sql` MCP tool:
SELECT properties.$pathname AS path, count() AS views FROM events WHERE event = '$pageview' AND timestamp > now() - INTERVAL 7 DAY GROUP BY path ORDER BY views DESC LIMIT 200
Scan the result for:
- numeric IDs: `/users/123`, `/orders/4242`
- UUIDs: `/sessions/8f3c1a3b-…`
- slugs: `/posts/why-i-love-posthog`
- dates: `/archive/2024-09-12`
- locales: `/en-US/`, `/fr-FR/`
- pagination: `?page=3`, `/page/3/`
3. Draft regex + alias
| Pattern | Example match | `regex` | `alias` | | ------------------- | ---------------------- | ---------------------------- | ---------------------- | | Numeric segment | `/users/123/profile` | `/users/\d+/profile` | `/users/<id>/profile` | | UUID v4 | `/sessions/8f3c1a3b-…` | `/sessions/[0-9a-f-]{36}` | `/sessions/<uuid>` | | Slug | `/posts/why-posthog` | `/posts/[a-z0-9-]+$` | `/posts/<slug>` | | ISO date | `/archive/2024-09-12` | `/archive/\d{4}-\d{2}-\d{2}` | `/archive/<date>` | | Locale prefix | `/en-US/about` | `^/[a-z]{2}-[A-Z]{2}/` | `/<locale>/` | | Trailing query/page | `/blog?page=3` | `\?page=\d+$` | (empty alias drops it) |
Anchoring rules of thumb:
- start the regex with `^` only when the segment must be at the beginning of
the path
- end with `$` to keep a generic rule (e.g. `\d+$`) from matching mid-path
segments
4. Test before saving
Three options, pick one:
- **Settings page tester**: `/settings/project#path_cleaning` has a built-in
"test path" input that replays the full ordered chain.
- **Project HogQL** (via `execute-sql`):
SELECT replaceRegexpAll('/users/42/profile', '/users/\d+/profile', '/users/<id>/profile')Chain `replaceRegexpAll` calls in the same order the rules will run if you want to verify multi-rule interaction.
- **Built-in AI helper**: there is already an `AiRegexHelper` modal accessible
from the rule editor (`Help me with Regex` button) that turns natural language into a regex. Suggest it to the user when they say "I don't know regex" — but always validate the output against real paths via the tester.
5. Order rules from most-specific to most-general
Sequential application means a generic rule placed first will swallow everything that should have hit a specific rule.
order=0 /users/me/profile → /users/me/profile (specific, runs first) order=1 /users/\d+/profile → /users/<id>/profile order=2 /users/[a-z0-9-]+ → /users/<slug> (catch-all, runs last)
If `/users/[a-z0-9-]+` ran first it would also match `/users/me/profile` and make the more specific rule unreachable.
6. Apply via MCP
Prefer the `path-cleaning-rules-update` tool. It reads the current rules, applies granular operations (`append`, `insert`, `replace`, `remove`, `reorder`), auto-numbers `order`, and — unless you pass `confirm: true` — returns a **preview** of the resulting rules without saving. Pass `sample_paths` to see how the resulting set rewrites real paths.
First call it without `confirm` to get the preview, surface that to the user, then re-send the same call with `"confirm": true` to save:
{
"oOfficial PostHog plugin for AI clients. Access PostHog products directly from your AI coding tool.
Other skills on ai-plugin.
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
Questions about managing-path-cleaning-rules.
- Which coding agents can use managing-path-cleaning-rules?
- managing-path-cleaning-rules installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

