-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathskill-usage-tracker.ts
More file actions
86 lines (81 loc) · 2.89 KB
/
Copy pathskill-usage-tracker.ts
File metadata and controls
86 lines (81 loc) · 2.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import type { Plugin } from "@opencode-ai/plugin"
import { appendFile, mkdir } from "node:fs/promises"
import path from "node:path"
import os from "node:os"
const LOG_PATH = path.join(os.homedir(), ".local", "share", "opencode", "skill-usage-tracker.jsonl")
const BUILTIN_COMMANDS = new Set(["init", "review"])
export default {
id: "skill-usage-tracker",
server: () => {
const logPath = LOG_PATH
async function append(record: Record<string, unknown>) {
await mkdir(path.dirname(logPath), { recursive: true })
await appendFile(logPath, JSON.stringify(record) + "\n")
}
async function readAll(): Promise<Record<string, unknown>[]> {
const file = Bun.file(logPath)
if (!(await file.exists())) return []
const text = await file.text()
return text
.trim()
.split("\n")
.filter(Boolean)
.map((line) => JSON.parse(line))
}
return Promise.resolve({
"command.execute.before": async (cmd: { command: string; sessionID: string; arguments: string }) => {
if (BUILTIN_COMMANDS.has(cmd.command)) return
await append({
timestamp: new Date().toISOString(),
sessionID: cmd.sessionID,
skillName: cmd.command,
source: "slash-command",
arguments: cmd.arguments,
})
},
"tool.execute.before": async (
toolInput: { tool: string; sessionID: string; callID: string },
output: { args: any },
) => {
if (toolInput.tool !== "skill") return
await append({
timestamp: new Date().toISOString(),
sessionID: toolInput.sessionID,
skillName: output.args?.name,
source: "tool-call",
})
},
tool: {
"skill-log": {
description:
"Query the skill invocation log. Returns tracked skill invocations with timestamps and session IDs.",
args: {
type: "object",
properties: {
limit: { type: "number", description: "Max entries to return (default: 50)" },
session: { type: "string", description: "Filter by session ID" },
},
},
async execute(args: { limit?: number; session?: string }) {
let entries = await readAll()
if (args.session) {
entries = entries.filter((e) => e.sessionID === args.session)
}
const limit = args.limit ?? 50
const sliced = entries.slice(-limit)
return {
title: `Skill invocation log (${sliced.length} entries)`,
output: sliced
.map(
(e: any) =>
`[${e.timestamp}] session=${e.sessionID} skill=${e.skillName} via=${e.source}`,
)
.join("\n"),
metadata: { entries: sliced, count: sliced.length },
}
},
},
},
})
},
}