| Skills used in a session | Partly There is no per-session skills list in the TUI. When you invoke a skill explicitly ($skill or the skill picker), its instructions are injected as a user message wrapped in <skill><name>...</name><path>...</path> and saved with the session in ~/.codex/sessions, so you can find explicit uses by searching the transcript. Implicit uses show up only as the commands or skills tool calls that read the skill. No hook event is specific to skills, but PreToolUse and PostToolUse see those tool calls. If you export OpenTelemetry logs, the codex.skill_invocation events carry conversation.id and turn.id, which gives you a cleaner per-session list. Source | Each session's transcript at ~/.claude/projects/<project>/<session>.jsonl records every tool call, so skills Claude loaded appear as Skill tool calls and ones you typed appear as /name prompts. To capture them live, add a PreToolUse or PostToolUse hook with matcher Skill for Claude-initiated skills plus a UserPromptExpansion hook, which fires with command_name when you type /skillname and bypasses PreToolUse. Transcripts are deleted after cleanupPeriodDays, so they are not a permanent usage log. Source | Partly There is no skill-specific session view or hook. /skills lists the skills the CLI discovered, and you invoke one by hand with /skill-name. When the agent applies a skill itself, it reads that skill's SKILL.md. That read shows up as a readToolCall in --print --output-format stream-json, and a preToolUse/postToolUse hook with matcher Read (or a beforeReadFile hook) can catch it by checking the path. The hook matchers cover tool types (Shell, Read, Write, Task, MCP:<tool>). None of them is for skills. On Enterprise teams, OpenTelemetry Export's cursor.skill.activated log carries cursor.conversation.id, so you can list the skills each session used. Source | When the model loads a skill, it is a skill tool call with input { id }, emitted as session.tool.called and then session.tool.success or session.tool.failed (metadata.name, metadata.directory). When you load a skill yourself with @skill-id, it is recorded as a skill message and a durable session.skill.activated event. Plugins can follow both through ctx.tool.hook("execute.after") and ctx.event.subscribe(), and opencode session export dumps the transcript. Source | Partly A /skill:name command is expanded into a user message wrapped in <skill name="..." location="...">, which the TUI shows as a collapsible skill block and HTML exports label as skill:; it is saved in the session JSONL as a normal message entry. When the model picks a skill on its own it just calls the read tool on that SKILL.md, so you only see it as a read tool call and toolResult on the skill's path. There is no dedicated skill entry type or skill event; extensions can watch input (raw /skill: text, before expansion) and tool_call for read on a SKILL.md path. Source |
|---|
| Local usage counts | Not supported Codex keeps no per-skill usage counter on disk. None of the local SQLite stores in ~/.codex (state_5, thread_history_1, logs_2, memories_1, queue_1, goals_1) has a skill table. The counts shown under /usage are fetched from your ChatGPT account and are not stored locally. Source | Claude Code keeps a skillUsage map in ~/.claude.json, keyed by skill or command name, with usageCount and lastUsedAt (Unix milliseconds) for each entry. /skill-doctor (v2.1.252+) turns this into a report of how often each skill is used and what it costs in context, and flags skills that have never been invoked. The report opens in the /plugin manager's Stats tab, or prints as text with -p. The counter is approximate: repeated uses of the same skill within 60 seconds in one session are counted once. Nested skills may be recorded under either <dir>:<name> or the bare <name> key. /skill-doctor needs feature-flag fetching and doesn't work over Remote Control. Source | Not supported The CLI does not keep per-skill usage counts on disk. ~/.cursor/cli-config.json, ~/.cursor/agent-cli-state.json and the per-chat store.db files under ~/.cursor/chats/ have no skill counter. Usage is counted only on Cursor's servers, through the team Analytics API and OpenTelemetry Export. To count locally, have a postToolUse hook append each Read of a SKILL.md to a file you own. Source | Partly There is no per-skill counter. opencode stats --tools shows a TOOL RELIABILITY table per tool name (calls, error rate, p50 duration), built from the local session database, and all skill loads fall under the single skill row. @skill activations are not counted there. Source | Not supported Pi keeps no per-skill usage counter on disk. The only record is the session JSONL files, which you would have to scan yourself for <skill name=...> user messages and read calls on SKILL.md files. /session shows session statistics such as messages and tokens, not skill use. Source |
|---|
| Account or team reports | In the TUI, open /usage and choose View analytics. The Skills section shows your own daily skill invocation counts (top 10, or top 6 on Enterprise), fetched from analytics/daily-skill-usage-metrics with workspace_user=true; you must be signed in with ChatGPT. Workspace admins can see plugin and skill invocation counts in the Admin Console under Analytics > Insights, in a category's details. The /usage Skills view sums counts by display name and shows only the top skills. Invocations aren't the same as messages or completed tasks, and plugin and skill allocations overlap, so don't add them together. Source | On the Enterprise plan, GET /v1/organizations/analytics/skills in the Claude Enterprise Analytics API returns daily per-skill rows. Filtered to product claude_code, they include invocation_count, distinct_session_skill_used_count, distinct_user_count and attributed spend. A Primary Owner creates the read:analytics key at claude.ai/analytics/api-keys. The claude.ai/analytics/claude-code dashboard and the Console Claude Code dashboard show no per-skill breakdown. Not available on the Team plan. invocation_count is null when invocation reporting isn't enabled for the organization. Names of members' private skills are withheld or shown as opaque ids. For individuals, /usage on Pro, Max, Team or Enterprise shows the share of recent usage attributed to each skill, computed from local history on that machine. Source | Enterprise teams can call the Analytics API endpoints GET https://api.cursor.com/analytics/team/skills and GET /analytics/by-user/skills with an admin API key. Both return daily usage counts per skill_name, the second broken down by user. The Usage Analytics dashboard has no skills chart, and other plans have no skill report. The Analytics API is for Enterprise teams only and needs an admin:* API key. Dates default to the last 7 days. Use startDate/endDate for other ranges. Source | Not supported OpenCode 2.x has no account, team or admin report of skill usage. opencode stats is local only. Source | Not supported Pi has no account, team or admin report of skill usage. The Radius gateway from Earendil Works offers organization-level analytics on model traffic, but nothing in Pi reports skill use to it. Source |
|---|
| Telemetry you can collect | Yes. Point [otel] exporter at an OTLP HTTP or gRPC collector and Codex sends a codex.skill_invocation log event for every explicit injection and every detected implicit use. The event carries skill.name, skill.invocation_type, skill.scope, skill.plugin_id, conversation.id, turn.id, the model and user/account IDs, but never skill contents or paths. Codex also records a codex.skill.injected counter metric, tagged with skill, status and invoke_type. The default otel.exporter = "none" exports nothing. Implicit detection from shell commands is best effort and counted at most once per skill per turn. Setting [analytics] enabled = false also turns off custom OTLP metrics exporters. Source | With CLAUDE_CODE_ENABLE_TELEMETRY=1 and an OTLP exporter, every invocation emits a claude_code.skill_activated event. The event carries skill.name, skill.source, and invocation_trigger (user-slash, claude-proactive or nested-skill), so slash-typed and model-initiated uses are both covered. The claude_code.cost.usage and claude_code.token.usage metrics also carry a skill.name attribute, and claude_code.tool_result events for the Skill tool include skill_name in tool_parameters. Names of user-defined and third-party plugin skills are redacted (custom_skill or third-party) unless you set OTEL_LOG_TOOL_DETAILS=1. Source | Enterprise admins can turn on OpenTelemetry Export in Team Settings > OpenTelemetry Export. Cursor's servers then send a cursor.skill.activated log (family skills_hooks_plugins, on by default) over OTLP/HTTP to a collector you run. Each log carries cursor.skill.name, cursor.skill.trigger (agent_read, manually_attached or skill_name_in_prompt), cursor.skill.source and cursor.conversation.id. The export runs from Cursor's servers. The CLI has no local OTLP setting. Delivery is at-least-once, so dedupe on cursor.event.id. Nothing from before the destination was set up is sent. Source | Partly The V1 experimental.openTelemetry option is accepted but unsupported in 2.x. With OTEL_EXPORTER_OTLP_ENDPOINT set, OpenCode exports a Tool.execute span that carries no tool or skill name. To collect skill invocations, use a plugin on ctx.tool.hook("execute.after") (check event.tool === "skill" and event.input.id) or the server event stream (session.tool.*, session.skill.activated). Source | Partly Pi has no OpenTelemetry export and no skill event; its built-in telemetry is limited to anonymous install/update reporting (enableInstallTelemetry, PI_TELEMETRY) and an opt-in enableAnalytics flag used only by the experimental first-run setup. To collect skill use yourself, write an extension that listens to input (for /skill:name) and tool_call/tool_result (for read on SKILL.md), or parse tool_execution_start/tool_execution_end from pi --mode json. Source |
|---|
| Whether a skill run succeeded | Partly Codex records only whether a skill was loaded, not whether its task worked. On codex.skill.injected, status="error" means an explicitly mentioned skill could not be injected. The codex.skill.turn.duration_seconds histogram tags the whole turn as completed, error or aborted, but per plugin rather than per skill. A codex.skill_invocation event shows that a skill was used, not that the task succeeded. Source | Partly When Claude calls the Skill tool, the claude_code.tool_result event reports success and error_type, and a PostToolUseFailure hook with matcher Skill fires if loading the skill fails. Those signals only show whether the skill was loaded. Nothing reports whether the work the skill describes was finished, and /name invocations you type don't go through the tool at all. skill_activated has no success or outcome attribute. Source | Not supported No. cursor.skill.activated and the Analytics API skill counts record only that a skill was used. Neither says whether it succeeded or failed. The closest signals are the postToolUseFailure hook and the stream-json result event (is_error), which cover single tool calls and the whole turn, not one skill. Source | Partly Each skill tool call ends in session.tool.success or session.tool.failed (for example Unable to load skill <id>), and plugin execute.after receives status: "completed" or "error". opencode stats --tools reports the error rate for the skill tool as a whole. That tells you whether the skill loaded, not whether the work it guided succeeded. Source | Partly Pi records no success or failure for a skill run as a whole. You can see whether loading it worked: a model-initiated load is a read tool result with isError, and if a /skill:name file cannot be read pi reports a skill_expansion error and sends the raw text unexpanded. Whether the task the skill guided succeeded has to be judged from the rest of the transcript. Source |
|---|