Database

March 6, 2026 ยท View on GitHub

uberSKILLS uses SQLite accessed via Drizzle ORM. The database file is stored locally at data/uberskills.db (configurable via DATABASE_URL).

Connection

The DATABASE_URL environment variable determines the driver:

  • file: prefix -- uses better-sqlite3 (local SQLite)
  • libsql:// prefix -- uses @libsql/client (Turso, for cloud deployment)

Default: file:data/uberskills.db

The database uses a singleton connection pattern. On first connection, it auto-creates the data/ directory and runs migrations.

Schema

There are 5 tables: skills, skill_files, skill_versions, test_runs, and settings.

Entity Relationships

skills (1) --> (N) skill_files
skills (1) --> (N) skill_versions
skills (1) --> (N) test_runs
settings (standalone key-value store)

All child tables use onDelete: "cascade" -- deleting a skill removes all associated files, versions, and test runs.

skills

ColumnTypeDescription
idtext (PK)nanoid
nametextHuman-readable skill name
slugtext (unique)URL-safe identifier, auto-generated from name
descriptiontextSkill description
triggertextWhen the skill should activate
tagstextJSON array of tag strings
model_patterntextOptional regex for model matching
contenttextSKILL.md markdown body (instructions)
statustextdraft / ready / deployed
created_atinteger (timestamp)Creation date
updated_atinteger (timestamp)Last modification date

skill_files

ColumnTypeDescription
idtext (PK)nanoid
skill_idtext (FK)References skills.id
pathtextRelative path, e.g. prompts/setup.md
contenttextFile content
typetextprompt / resource
created_atinteger (timestamp)Creation date
updated_atinteger (timestamp)Last modification date

skill_versions

ColumnTypeDescription
idtext (PK)nanoid
skill_idtext (FK)References skills.id
versionintegerAuto-incremented per skill
content_snapshottextFull SKILL.md at this version
metadata_snapshottextJSON of frontmatter at this version
change_summarytextDescription of changes
created_atinteger (timestamp)Creation date

test_runs

ColumnTypeDescription
idtext (PK)nanoid
skill_idtext (FK)References skills.id
modeltextModel ID, e.g. anthropic/claude-sonnet-4
system_prompttextResolved system prompt sent to model
user_messagetextUser's test message
assistant_responsetextAI response (null while streaming)
argumentstextJSON of substituted arguments
prompt_tokensintegerInput token count
completion_tokensintegerOutput token count
total_tokensintegerTotal token count
latency_msintegerTotal response time
ttft_msintegerTime to first token
statustextrunning / completed / error
errortextError message if status is error
created_atinteger (timestamp)Creation date

settings

ColumnTypeDescription
keytext (PK)Setting key
valuetextSetting value
encryptedinteger (boolean)Whether the value is encrypted
updated_atinteger (timestamp)Last modification date

Known keys:

KeyEncryptedDescription
openrouter_api_keyYesOpenRouter API key
default_modelNoDefault model ID for testing
themeNoUI theme: light / dark / system

Conventions

IDs

All entities use nanoid() for primary keys (21-character alphanumeric string). Slugs are auto-generated from skill names (lowercase, hyphenated, with numeric suffix on collision).

Timestamps

Timestamp columns use integer("column", { mode: "timestamp" }) with $defaultFn(() => new Date()).

JSON Fields

Arrays and objects (tags, arguments) are stored as JSON strings in text columns. Parse with JSON.parse(), serialize with JSON.stringify().

Queries

Query functions are in packages/db/src/queries/*.ts. Each file exports typed CRUD functions. All queries use Drizzle's typed query builder -- never raw SQL. Search uses SQLite LIKE on name, description, and tags fields.