@crafter/skillkit 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/bin.ts +5 -0
- package/src/commands/scan.ts +31 -0
- package/src/commands/stats.ts +27 -6
- package/src/db/queries.ts +9 -6
package/package.json
CHANGED
package/src/bin.ts
CHANGED
|
@@ -24,6 +24,11 @@ function printHelp(): void {
|
|
|
24
24
|
${cyan("version")} Print version
|
|
25
25
|
${cyan("help")} Show this help message
|
|
26
26
|
|
|
27
|
+
${bold("FLAGS")}
|
|
28
|
+
${dim("scan")} ${cyan("--include-commands")} Also track slash commands (not just skills)
|
|
29
|
+
${dim("stats")} ${cyan("--days N")} Time range in days (default: 30)
|
|
30
|
+
${dim("stats")} ${cyan("--all")} Show all skills, not just top 10
|
|
31
|
+
|
|
27
32
|
${dim("Install skills via skills.sh: npx skills add <owner/repo>")}
|
|
28
33
|
`);
|
|
29
34
|
}
|
package/src/commands/scan.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
2
3
|
import { basename, join } from "node:path";
|
|
3
4
|
import { upsertInstalledSkill, deduplicateInvocations } from "../db/queries";
|
|
4
5
|
import { getDb } from "../db/schema";
|
|
@@ -22,6 +23,7 @@ function detectSource(skillPath: string): "skills.sh" | "manual" {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export async function runScan(): Promise<void> {
|
|
26
|
+
const includeCommands = process.argv.includes("--include-commands");
|
|
25
27
|
const db = getDb();
|
|
26
28
|
|
|
27
29
|
const agents = getDetectedAgents();
|
|
@@ -103,6 +105,35 @@ export async function runScan(): Promise<void> {
|
|
|
103
105
|
} catch {}
|
|
104
106
|
}
|
|
105
107
|
|
|
108
|
+
if (includeCommands) {
|
|
109
|
+
const commandDirs = [
|
|
110
|
+
join(process.cwd(), ".claude", "commands"),
|
|
111
|
+
join(homedir(), ".claude", "commands"),
|
|
112
|
+
];
|
|
113
|
+
let commandCount = 0;
|
|
114
|
+
for (const dir of commandDirs) {
|
|
115
|
+
if (!existsSync(dir)) continue;
|
|
116
|
+
try {
|
|
117
|
+
for (const e of readdirSync(dir)) {
|
|
118
|
+
if (e.endsWith(".md")) {
|
|
119
|
+
knownSkills.add(e.slice(0, -3));
|
|
120
|
+
commandCount++;
|
|
121
|
+
} else {
|
|
122
|
+
try {
|
|
123
|
+
if (statSync(join(dir, e)).isDirectory()) {
|
|
124
|
+
knownSkills.add(e);
|
|
125
|
+
commandCount++;
|
|
126
|
+
}
|
|
127
|
+
} catch {}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
} catch {}
|
|
131
|
+
}
|
|
132
|
+
if (commandCount > 0) {
|
|
133
|
+
console.log(dim(` + ${commandCount} slash commands`));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
106
137
|
const removed = deduplicateInvocations(db);
|
|
107
138
|
if (removed > 0) {
|
|
108
139
|
console.log(` ${dim(`Cleaned ${removed} duplicate entries`)}`);
|
package/src/commands/stats.ts
CHANGED
|
@@ -23,15 +23,32 @@ function getMostActiveDay(db: ReturnType<typeof getDb>): string {
|
|
|
23
23
|
return days[parseInt(row.day, 10)] ?? "N/A";
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
function parseDays(args: string[]): number {
|
|
27
|
+
for (let i = 0; i < args.length; i++) {
|
|
28
|
+
if (args[i] === "--days" && args[i + 1]) {
|
|
29
|
+
const n = parseInt(args[i + 1], 10);
|
|
30
|
+
if (!isNaN(n) && n > 0) return n;
|
|
31
|
+
}
|
|
32
|
+
const match = args[i]?.match(/^--days=(\d+)$/);
|
|
33
|
+
if (match) {
|
|
34
|
+
const n = parseInt(match[1], 10);
|
|
35
|
+
if (!isNaN(n) && n > 0) return n;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return 30;
|
|
39
|
+
}
|
|
40
|
+
|
|
26
41
|
export async function runStats(): Promise<void> {
|
|
27
42
|
const db = getDb();
|
|
43
|
+
const days = parseDays(process.argv.slice(3));
|
|
44
|
+
|
|
28
45
|
console.log("\n Scanning sessions...");
|
|
29
46
|
const newCount = await scanAllSessions(db);
|
|
30
47
|
if (newCount > 0) {
|
|
31
48
|
console.log(` Found ${newCount} new invocations.\n`);
|
|
32
49
|
}
|
|
33
50
|
|
|
34
|
-
const stats = getSkillStats(db,
|
|
51
|
+
const stats = getSkillStats(db, days);
|
|
35
52
|
|
|
36
53
|
if (stats.total === 0) {
|
|
37
54
|
console.log(`\n ${yellow("No analytics data yet.")}`);
|
|
@@ -39,20 +56,24 @@ export async function runStats(): Promise<void> {
|
|
|
39
56
|
return;
|
|
40
57
|
}
|
|
41
58
|
|
|
42
|
-
const
|
|
59
|
+
const showAll = process.argv.includes("--all");
|
|
60
|
+
const topSkills = getTopSkills(db, days, showAll ? undefined : 10);
|
|
43
61
|
const activeDay = getMostActiveDay(db);
|
|
44
62
|
|
|
45
|
-
|
|
63
|
+
const label =
|
|
64
|
+
days === 30 ? "last 30 days" : days === 7 ? "last 7 days" : `last ${days} days`;
|
|
65
|
+
|
|
66
|
+
console.log(`\n ${bold("SKILL-KIT ANALYTICS")} ${dim(`(${label})`)}\n`);
|
|
46
67
|
console.log(` Total invocations: ${bold(String(stats.total))}`);
|
|
47
68
|
console.log(` Unique skills: ${bold(String(stats.unique_skills))}`);
|
|
48
69
|
console.log(` Most active day: ${bold(activeDay)}\n`);
|
|
49
|
-
console.log(` ${bold("TOP SKILLS")}\n`);
|
|
70
|
+
console.log(` ${bold(showAll ? "ALL SKILLS" : "TOP SKILLS")}\n`);
|
|
50
71
|
|
|
51
72
|
const maxCount = topSkills.length > 0 ? (topSkills[0]?.total ?? 1) : 1;
|
|
52
73
|
const barWidth = 20;
|
|
53
74
|
|
|
54
|
-
for (const skill of topSkills
|
|
55
|
-
const daily = getDailyUsage(db, skill.skill_name,
|
|
75
|
+
for (const skill of topSkills) {
|
|
76
|
+
const daily = getDailyUsage(db, skill.skill_name, days);
|
|
56
77
|
const filled = Math.round((skill.total / maxCount) * barWidth);
|
|
57
78
|
const bar = "█".repeat(filled);
|
|
58
79
|
const spark = sparkline(daily.map((d) => d.count));
|
package/src/db/queries.ts
CHANGED
|
@@ -24,15 +24,18 @@ interface InstalledSkillRow {
|
|
|
24
24
|
size_bytes: number | null;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export function getTopSkills(
|
|
27
|
+
export function getTopSkills(
|
|
28
|
+
db: Database,
|
|
29
|
+
days = 30,
|
|
30
|
+
limit?: number,
|
|
31
|
+
): TopSkillRow[] {
|
|
28
32
|
const cutoff = new Date(
|
|
29
33
|
Date.now() - days * 24 * 60 * 60 * 1000,
|
|
30
34
|
).toISOString();
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
.all(cutoff);
|
|
35
|
+
const sql = limit
|
|
36
|
+
? `SELECT skill_name, COUNT(*) as total FROM skill_invocations WHERE timestamp >= ? GROUP BY skill_name ORDER BY total DESC LIMIT ${limit}`
|
|
37
|
+
: "SELECT skill_name, COUNT(*) as total FROM skill_invocations WHERE timestamp >= ? GROUP BY skill_name ORDER BY total DESC";
|
|
38
|
+
return db.query<TopSkillRow, [string]>(sql).all(cutoff);
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
export function getSkillStats(db: Database, days = 30): StatsRow {
|