Working With Skills

作者 PostHog469d1773e9cb無授權條款收錄於 2026年10月8日更新於 2026年10月8日

Best practices for agents managing PostHog skills via the MCP `skill-*` tools — how to discover, read, create, update, and refactor skills efficiently, especially large skills with many bundled files. Use whenever you are about to call any `skill-*` tool, asked to author or edit a shared skill, or troubleshoot why a skill write was rejected. Pairs with `skills-store` (which covers the raw tool surface) by adding the decision-tree, efficiency, and pitfall guidance.

僅含說明AI & Agents
AI 產生的概覽

指導代理使用 PostHog 的 skill-* MCP 工具來探索、讀取、建立、更新與重構技能。

功能
此技能為透過 skill-* MCP 工具操作 PostHog 技能的代理提供最佳實務指引。內容涵蓋選擇合適工具的決策樹、有效率地閱讀大型多檔案技能、撰寫規則、增量與整體更新、透過 base_version 處理並行、常見陷阱、封存,以及將本機 SKILL.md 目錄移植到 PostHog。它產出的是指引與工作流程說明,而非檔案或程式碼。
適用情境
在呼叫任何 skill-* 工具之前、被要求撰寫或編輯共用技能時,或排查技能寫入遭拒的問題時使用。處理內含大量檔案的技能時也適用。
執行需求
無需指令碼或特殊執行環境,僅為說明性內容。它假定可存取 PostHog 的 skill-* MCP 工具,並引用搭配的 skills-store 技能以了解原始工具介面。

Working with PostHog skills

This skill teaches agents how to use the skill-* MCP tools well — minimum context, minimum round-trips, minimum mistakes. If you are not yet familiar with the tool surface itself, read the skills-store skill first for the catalog. This document is about how to choose between the tools and how to scale the workflow when skills get big.

Operating principles

  1. Progressive disclosure is non-negotiable. Lists return descriptions, get returns body + manifest, file-get returns one file. Never preload bundled files "just in case" — every preloaded script is wasted context for the actual task.
  2. Pick the smallest write primitive that does the job. A targeted edits or file_edits is cheaper, safer, and clearer in version history than a full body or full bundle replacement.
  3. Reads are cheap; concurrent overwrites are not. Always have a recent version from skill-get (or from the response of the previous write) before calling any write tool, and pass it as base_version.
  4. Authoring follows the Agent Skills spec. Keep name kebab-case, descriptions trigger-rich, body short, bulky material in bundled files.

Decision tree: which tool do I call?

text
Need to know what's available?  └─► skill-list                    (names + descriptions only)
Need to use / inspect a specific skill?  └─► skill-get                     (body + file manifest, NO file contents)        └─► skill-file-get          (one file, on demand, only as referenced)
Authoring a brand new skill?  └─► skill-create                  (body + all initial files in one call)
Editing an existing skill?  ├─ Body change?  │    ├─ Substantial rewrite ............. update(body=...)  │    └─ Surgical tweak .................. update(edits=[{old, new}, ...])  ├─ Bundled file content change?  │    └─ update(file_edits=[{path, edits:[...]}, ...])  ├─ Add / remove / rename a file?  │    ├─ Add ............................. skill-file-create  │    ├─ Delete .......................... skill-file-delete  │    └─ Rename .......................... skill-file-rename  └─ Wholesale bundle reset (rare!) ....... update(files=[...])  # replaces ALL files
Renaming the skill itself?  └─► skill-rename                  (keeps versions, files, and owners)
Want a fork as the starting point?  └─► skill-duplicate               (then update the copy)
Done with a skill entirely?  └─► skill-archive                 (hides ALL versions; cannot be undone)

If you find yourself reaching for update(body=...) plus a sprawling files=[...] to change one paragraph and one script, stop — that's two narrower calls (update(edits=[...]) plus update(file_edits=[...])) or even a single update carrying both edits and file_edits.

Discover before you fetch

json
posthog:skill-list{ "search": "fractal" }

skill-list is the right tool to "find a skill" — it returns names and descriptions only. Reading the descriptions is the entire point: pick the right skill before pulling any body. If search doesn't narrow it enough, list without it and scan, but do not start fetching candidate bodies blindly.

skill-get should be called once per skill per task, not per question. Cache the body in your working memory; fetch again only if you suspect the skill changed under you (e.g. a 409 on write — see "Concurrency" below).

Reading a large skill efficiently

Big skills (long body, many bundled files) are the case where lazy loading matters most.

  1. skill-get(skill_name=...) — read body + files[] manifest.
  2. Scan the body's table of contents / headings. The body should already tell you which file goes with which task — that's why bodies stay short and reference files by path.
  3. For each file the body explicitly points at for the current task, call skill-file-get(file_path=...). Skip everything else.
  4. If the body references "see scripts/X for the rare case Y" and you are not in case Y, do not fetch scripts/X.

When in doubt, fewer files. You can always fetch one more on the next turn.

Authoring a new skill

Use a single skill-create call with body and initial files — the skill lands at version: 1 complete. Do not create the skill empty and then make N follow-up skill-file-create calls; that's N extra versions and N extra round-trips for no benefit.

json
posthog:skill-create{  "name": "my-skill",  "description": "What it does AND when to use it. Include trigger keywords.",  "body": "# my-skill\n\n## When to use\n...\n## Workflow\n...",  "license": "MIT",  "compatibility": "Requires Python 3.10+",  "allowed_tools": ["Bash", "Write"],  "metadata": { "author": "me", "category": "..." },  "files": [    { "path": "scripts/foo.py", "content": "...", "content_type": "text/x-python" },    { "path": "references/primer.md", "content": "...", "content_type": "text/markdown" }  ]}

Authoring rules of thumb

  • description is the discovery surface. It is the only thing skill-list returns. Make it trigger-rich (what the user might say) and scope-honest (what the skill does and does not do).
  • name — kebab-case, max 64 chars, no leading/trailing/consecutive hyphens. The spec validator rejects anything else.
  • Body ≤ ~500 lines. Long preambles, exhaustive SQL, full example payloads, and runnable code belong in references/, assets/, or scripts/. The body should route to those files, not inline them.
  • File layout convention — scripts/ for executable code, references/ for prose docs and examples, assets/ for templates / data. Agents can rely on this for orientation when they only have the manifest.
  • allowed_tools lists the MCP / built-in tools the skill expects to be callable. Be honest — under-declaring causes silent failures, over-declaring is a security smell.
  • End with a ## Related skills footer when adjacent skills exist: a short bullet list of `skill-name` entries, each with a one-line handoff reason ("when to jump there"), so one skill invocation seeds discovery of the next. Refer to skills by name only (no paths — related skills often live in other products), and only list genuine next steps, not everything in the product.

Updating an existing skill

The single most common mistake is using update(body=..., files=[...]) for a small change. That works, but it round-trips the entire skill, makes the diff unreadable in version history, and risks dropping files if files was incomplete. Use the smallest primitive instead.

Always read first, capture version

json
posthog:skill-get{ "skill_name": "my-skill" }

Note the returned version — pass it as base_version on every write. After a successful write, the response contains the new version; chain further writes with that.

Body: full replacement vs incremental edits

Full replacement when you are restructuring the body:

json
posthog:skill-update{ "skill_name": "my-skill", "body": "# my-skill\n\nNew body...", "base_version": 7 }

Incremental edits when you are tweaking a few lines (preferred for small changes — easier to review, lower error surface):

json
posthog:skill-update{  "skill_name": "my-skill",  "edits": [    { "old": "Use Pillow for rendering.", "new": "Use Pillow ≥10.0 for rendering." },    { "old": "## Old section title", "new": "## New section title" }  ],  "base_version": 7}

Each edits[].old must match exactly once in the current body, and body and edits are mutually exclusive in one call.

Bundled file content edits

file_edits patches one or more existing files in place — non-targeted files carry forward unchanged. This is the right primitive when you are tweaking script logic or fixing a typo in a reference doc:

json
posthog:skill-update{  "skill_name": "my-skill",  "file_edits": [    {      "path": "scripts/foo.py",      "edits": [{ "old": "ITERATIONS = 100", "new": "ITERATIONS = 250" }]    },    {      "path": "references/primer.md",      "edits": [{ "old": "## Outdated header", "new": "## Updated header" }]    }  ],  "base_version": 7}

file_edits cannot add, remove, or rename files — only patch existing ones. For structural changes, use the per-file tools.

Combining edits in a single call

You can combine edits (body) and file_edits (existing files) in one skill-update call to publish a single coherent version when a change spans both:

json
posthog:skill-update{  "skill_name": "my-skill",  "edits": [{ "old": "## Configuration", "new": "## Setup" }],  "file_edits": [    { "path": "scripts/run.py", "edits": [{ "old": "DEBUG = False", "new": "DEBUG = True" }] }  ],  "base_version": 7}

File-path parameter naming (read this before guessing)

The same concept — a bundled file's path — is named differently depending on where it travels in the request, and this trips up agents working from memory. There is one rule:

  • file_path — when the path is part of the URL (skill-file-get, skill-file-delete). These read/delete one file addressed by its path.
  • path — when the path is a body field: skill-file-create, the files=[{path, content, content_type}] array, and file_edits=[{path, edits}].
  • old_path / new_path — body fields on skill-file-rename.

Mnemonic: path is the field name on a file object (it sits next to content), so everything that carries a file object uses path; the two tools that address a file by URL use file_path. When unsure, check the tool's input schema rather than guessing — passing path to file-get yields a /files/undefined/ 404.

Adding, removing, renaming files

Each is its own call, each publishes a new version:

json
posthog:skill-file-create{ "skill_name": "my-skill", "path": "scripts/julia.py", "content": "...", "base_version": 7 }
json
posthog:skill-file-delete{ "skill_name": "my-skill", "file_path": "scripts/old.py", "base_version": 8 }
json
posthog:skill-file-rename{ "skill_name": "my-skill", "old_path": "scripts/julia.py", "new_path": "scripts/julia_set.py", "base_version": 9 }

skill-file-rename is a true move — it carries the existing content forward without resending it. Always prefer it over delete + create when the content is unchanged.

When to use update(files=[...]) (rare)

Passing files to skill-update replaces the entire bundle — anything not in the array is dropped. This is the right tool only when you are intentionally wiping and reseeding the bundle (e.g. importing a fresh local SKILL.md tree). For almost every other case, prefer file_edits plus per-file CRUD.

Working with large multi-file skills

Skills with many files (10+) require extra discipline:

  • Treat the manifest as the index. skill-get's files[] is your map. Match each task step to one file and fetch only that one.
  • Group structural changes into a sequence, not a fork. If you are renaming three files, do them sequentially: rename → rename → rename, each chained via the previous response's version. That gives you three small reviewable versions instead of one giant update(files=[...]) blob.
  • Keep edits localised. A single skill-update with file_edits targeting five files is fine. A single update(files=[...]) carrying ten full file bodies is almost always a sign you should have used file_edits.
  • Refactor the body itself first. If the body has grown past ~500 lines, the right next step is usually to split content into new bundled files before adding more material.

Concurrency: base_version

Every write tool accepts base_version. Always pass it.

  • The server compares base_version to the current latest version. If they match, the write succeeds and the new version is base_version + 1.
  • If they differ, the write is rejected (someone else updated the skill). Re-run skill-get, reconcile your changes against the new body, and retry with the fresh version.
  • After a successful write, the response includes the new version. Chain further edits with that — do not re-get between back-to-back writes you control.

Skipping base_version does not speed things up — it just turns a clean "someone else won the race" error into a silent overwrite of their work.

Common pitfalls

  • Calling skill-list with no search and then fetching every body — defeats progressive disclosure. Read the descriptions first.
  • Pre-fetching every bundled file after skill-get — same mistake on the inner level. Fetch on demand from the body's directives.
  • Using update(body=..., files=[...]) for a one-line fix — round-trips the entire skill, makes diffs unreadable, and risks dropping files. Use edits / file_edits.
  • Using update(files=[...]) when you meant to add one file — drops every file you didn't include. Use skill-file-create instead.
  • Delete + create instead of rename — loses content history and costs an extra version bump.
  • Stale base_version after chained writes — read the version from the previous write's response, not from your initial get.
  • Leaving base_version off — accepts a silent overwrite. Always include it once you've done a get.
  • Empty / vague description — the skill becomes effectively undiscoverable via skill-list search. Treat the description as the trigger contract.
  • Long body + no bundled files — when a body crosses ~500 lines, refactor into references/ and scripts/ rather than letting it grow.
  • Mixing body and edits in one update call — they're mutually exclusive. Pick one.
  • Guessing path vs file_path — file-get and file-delete take file_path (it's in the URL); create, rename (old_path/new_path), files, and file_edits take path (it's a body field). See "File-path parameter naming" above.

Archiving a skill

skill-archive hides every active version of a skill by name. It is not version-scoped and cannot be undone — the skill drops out of skill-list and skill-get for the whole team.

json
posthog:skill-archive{ "skill_name": "my-skill" }

Before archiving, skill-get the skill if you need to inspect or copy it first. Archiving is the right tool for retiring a skill entirely; to remove a single bundled file use skill-file-delete, and to roll back content publish a new version rather than archiving.

Porting a local SKILL.md tree into PostHog

When migrating a local skill folder (e.g. my-skill/SKILL.md plus scripts/, references/, assets/):

  1. Read the local SKILL.md. Its frontmatter maps to name, description, license, compatibility, allowed_tools, metadata. The body after the frontmatter becomes body.
  2. Walk the bundled subdirs and gather every file as { path, content, content_type }.
  3. Call posthog:skill-create once with everything — the skill lands at version: 1 complete. Do not split this into a create + N file-create calls.

After the create, the skill is live for everyone via skill-get.

When a skill is the wrong answer

Not every persistent prompt belongs in the skills store:

  • One-off task instructions belong in the conversation, not in a skill.
  • Personal scratchpads belong in agent memory or local files.
  • Code is not a skill — if it's something a service runs, it belongs in the repo.

A good skill is reusable, discoverable by description, and worth the cost of keeping it correct over time.

來源與署名

來源:PostHog/ai-plugin位於skills/working-with-skills提交469d177

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架

更多來自 PostHog/ai-plugin 的技能

Writing Simplified Technical English

PostHog

套用 ASD-STE100 簡化技術英語規則,讓代理撰寫的文字語意明確、方便執行。

Writing & Content2026年10月8日

Working With Task Comments

PostHog

透過 PostHog MCP exec 調度器讀取並解讀 PostHog 任務、成品和畫布上的留言。

Productivity & Workflow2026年10月8日

Working With Scouts

PostHog

說明如何把監看工作委派給 PostHog Signals 偵察代理、處理其回報,並長期調校整個代理團隊的操作手冊。

AI & Agents2026年10月8日

Validating And Publishing Canvases

PostHog

Validate and publish a canvas source project safely: the source-project shape, declared capabilities, reading the current version pointer, iterating on validation diagnostics, guarded publishing with expected_current_version_id, staging a draft build and promoting it, waiting out the queued build, and recovering from a 409 version_conflict or a 429 capacity limit without overwriting concurrent work. Use whenever a canvas edit is ready to save, a draft build is wanted, a canvas publish or build returns diagnostics or a conflict, or a task needs to understand canvas version history.

待分類2026年10月8日

Understanding Billing Usage

PostHog

Explains PostHog billing usage and spend from the customer's visible Billing MCP tools. Use when the user asks why usage or spend is high, which product or project is driving usage, what a usage type means, how to reduce usage, what changed over time, why they got a usage change alert, or whether a spike/drop alert was real or noisy. Also use before product-specific analytics skills when the user names a billable PostHog product metric such as events, recordings, feature flag requests, exceptions, survey responses, synced rows, logs, AI events, AI credits, or Inbox credits. Starts from Billing usage/spend tools, then routes to customer-visible product MCP surfaces for deeper investigation.

待分類2026年10月8日

Turning Engineering Analytics Into Insights

PostHog

將工程分析資料轉換為已儲存的 PostHog SQL 洞察、儀表板和訂閱,並提供 HogQL 指引。

Data & Analytics2026年10月8日