nexus/server/src/services/skill-registry-schema.ts
Mikkel Georgsen b38a230b77 feat(12-01): personalRatings schema, DB DDL, skillRatingService, and tests
- Add personalRatings table to skill-registry-schema.ts
- Add taskCount, avgCostUsd, lastUsedAt columns to agentSkills in schema
- Add CREATE_PERSONAL_RATINGS_TABLE DDL constant in skill-registry-db.ts
- Add ALTER TABLE statements for new agent_skills usage columns (idempotent)
- Create skill-registry-ratings.ts with skillRatingService factory
- rate() appends personal rating, validates stars 1-5
- getRatings() returns ratings ordered by createdAt DESC
- recordUsageForAgent() atomically updates task_count, avg_cost_usd, last_used_at
- All 8 tests pass
2026-04-01 07:46:09 +02:00

91 lines
3.1 KiB
TypeScript

import { sqliteTable, text, integer, real, primaryKey } from "drizzle-orm/sqlite-core";
export const skills = sqliteTable("skills", {
id: text("id").primaryKey(),
sourceId: text("source_id").notNull(),
name: text("name").notNull(),
description: text("description"),
sourceUrl: text("source_url"),
activeVersionId: text("active_version_id"),
removedAt: integer("removed_at"), // unix ms, nullable — soft-delete
createdAt: integer("created_at").notNull(),
updatedAt: integer("updated_at").notNull(),
});
export const skillVersions = sqliteTable("skill_versions", {
id: text("id").primaryKey(),
skillId: text("skill_id").notNull(),
version: text("version").notNull(),
fetchedAt: integer("fetched_at").notNull(),
cacheDir: text("cache_dir"),
});
export const skillFiles = sqliteTable("skill_files", {
id: text("id").primaryKey(),
versionId: text("version_id").notNull(),
path: text("path").notNull(),
kind: text("kind").notNull(), // "skill" | "reference" | "script" | "asset"
sizeBytes: integer("size_bytes"),
});
export const communityRatings = sqliteTable("community_ratings", {
id: text("id").primaryKey(),
skillId: text("skill_id").notNull(),
fetchedAt: integer("fetched_at").notNull(),
averageRating: real("average_rating"),
ratingCount: integer("rating_count"),
source: text("source"),
});
export const skillGroups = sqliteTable("skill_groups", {
id: text("id").primaryKey(),
name: text("name").notNull(),
description: text("description"),
isBuiltin: integer("is_builtin").notNull().default(0),
createdAt: integer("created_at").notNull(),
updatedAt: integer("updated_at").notNull(),
});
export const skillGroupMembers = sqliteTable("skill_group_members", {
groupId: text("group_id").notNull(),
skillId: text("skill_id").notNull(),
addedAt: integer("added_at").notNull(),
}, (t) => ({
pk: primaryKey({ columns: [t.groupId, t.skillId] }),
}));
export const skillGroupInheritance = sqliteTable("skill_group_inheritance", {
childGroupId: text("child_group_id").notNull(),
parentGroupId: text("parent_group_id").notNull(),
}, (t) => ({
pk: primaryKey({ columns: [t.childGroupId, t.parentGroupId] }),
}));
export const agentSkillGroups = sqliteTable("agent_skill_groups", {
agentId: text("agent_id").notNull(),
groupId: text("group_id").notNull(),
assignedAt: integer("assigned_at").notNull(),
}, (t) => ({
pk: primaryKey({ columns: [t.agentId, t.groupId] }),
}));
export const agentSkills = sqliteTable("agent_skills", {
agentId: text("agent_id").notNull(),
skillId: text("skill_id").notNull(),
installedAt: integer("installed_at").notNull(),
taskCount: integer("task_count").notNull().default(0),
avgCostUsd: real("avg_cost_usd"),
lastUsedAt: integer("last_used_at"),
}, (t) => ({
pk: primaryKey({ columns: [t.agentId, t.skillId] }),
}));
export const personalRatings = sqliteTable("personal_ratings", {
id: text("id").primaryKey(),
skillId: text("skill_id").notNull(),
versionId: text("version_id"),
stars: integer("stars").notNull(), // 1-5
note: text("note"),
createdAt: integer("created_at").notNull(),
updatedAt: integer("updated_at").notNull(),
});