Skip to content

Commit 2e73f2e

Browse files
suryaiyer95claude
andcommitted
feat: add skill_used telemetry event
Tracks which skill is loaded and where it came from (`builtin`, `global`, or `project`) with duration. Wrapped in try/catch — cannot break skill loading. Docs table updated. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent afe04ad commit 2e73f2e

4 files changed

Lines changed: 33 additions & 1 deletion

File tree

docs/docs/configure/telemetry.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ We collect the following categories of events:
3333
| `error_recovered` | Successful recovery from a transient error (error type, strategy, attempt count) |
3434
| `mcp_server_census` | MCP server capabilities after connect (tool and resource counts — no tool names) |
3535
| `context_overflow_recovered` | Context overflow is handled (strategy) |
36+
| `skill_used` | A skill is loaded (skill name and source — `builtin`, `global`, or `project` — no skill content) |
3637
| `core_failure` | A tool failure occurs — error category, error message (truncated to 500 chars), and PII-masked arguments (string literals in SQL replaced with `?`, sensitive keys like `password`/`token`/`secret` fully redacted) |
3738

3839
Each event includes a timestamp, anonymous session ID, and the CLI version.

packages/drivers/src/sqlserver.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import type { ConnectionConfig, Connector, ConnectorResult, SchemaColumn } from
77
export async function connect(config: ConnectionConfig): Promise<Connector> {
88
let mssql: any
99
try {
10-
// @ts-expect-error — optional dependency, loaded at runtime
1110
mssql = await import("mssql")
1211
mssql = mssql.default || mssql
1312
} catch {

packages/opencode/src/altimate/telemetry/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,15 @@ export namespace Telemetry {
332332
has_ssh_tunnel: boolean
333333
has_keychain: boolean
334334
}
335+
| {
336+
type: "skill_used"
337+
timestamp: number
338+
session_id: string
339+
message_id: string
340+
skill_name: string
341+
skill_source: "builtin" | "global" | "project"
342+
duration_ms: number
343+
}
335344
| {
336345
type: "core_failure"
337346
timestamp: number

packages/opencode/src/tool/skill.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,16 @@ import { iife } from "@/util/iife"
99
import { Fingerprint } from "../altimate/fingerprint"
1010
import { Config } from "../config/config"
1111
import { selectSkillsWithLLM } from "../altimate/skill-selector"
12+
import { Telemetry } from "../altimate/telemetry"
13+
import os from "os"
1214

1315
const MAX_DISPLAY_SKILLS = 50
16+
17+
function classifySkillSource(location: string): "builtin" | "global" | "project" {
18+
if (location.includes("node_modules") || location.includes(".altimate/builtin")) return "builtin"
19+
if (location.startsWith(os.homedir())) return "global"
20+
return "project"
21+
}
1422
// altimate_change end
1523

1624
export const SkillTool = Tool.define("skill", async (ctx) => {
@@ -83,6 +91,7 @@ export const SkillTool = Tool.define("skill", async (ctx) => {
8391
description,
8492
parameters,
8593
async execute(params: z.infer<typeof parameters>, ctx) {
94+
const startTime = Date.now()
8695
// altimate_change start - use upstream Skill.get() for exact name lookup
8796
const skill = await Skill.get(params.name)
8897

@@ -122,6 +131,20 @@ export const SkillTool = Tool.define("skill", async (ctx) => {
122131
return arr
123132
}).then((f) => f.map((file) => `<file>${file}</file>`).join("\n"))
124133

134+
try {
135+
Telemetry.track({
136+
type: "skill_used",
137+
timestamp: Date.now(),
138+
session_id: ctx.sessionID,
139+
message_id: ctx.messageID,
140+
skill_name: skill.name,
141+
skill_source: classifySkillSource(skill.location),
142+
duration_ms: Date.now() - startTime,
143+
})
144+
} catch {
145+
// Telemetry must never break skill loading
146+
}
147+
125148
return {
126149
title: `Loaded skill: ${skill.name}`,
127150
output: [

0 commit comments

Comments
 (0)