Sqlite db generator
Claude Agent Skills
npx -y skills add alycd/agent-skills --skill sqlite-db-generatorAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Design and build a local SQLite database — brainstorm entities, define schemas, write records, query data, and expose a standard interface other skills can consume. Use this skill whenever the user wants to persist structured data locally, think through a data model, create or update a SQLite schema, insert or update records, run queries, bulk import/export data, or build a lightweight local store for an agent or CLI tool. Also trigger when the user edits schema.sql and wants the skill and db to reflect those changes. Trigger phrases: "create a sqlite db", "set up a local database", "store this in sqlite", "help me design a schema", "what tables do I need", "save records to a db", "query my local db", "update the schema", "add a table", "sync the schema", "export my data", "import records".
SKILL.md
22.6 KB, ~5.5k tokens by cl100k_base, as published. Nobody here has run it
SQLite Store Skill
A skill for designing and building a local SQLite database. Always start with the brainstorming
phase — rushing to tables before understanding the domain produces schemas that need to be
rewritten. The schema.sql file is the single source of truth once design is done. After any
schema change, run the sync command to keep the db and this skill in sync.
Other skills can consume this db via the standard context and schema commands — no Markdown
parsing required.
File Layout
Local scope (default) — DB belongs to a specific project
<project>/
├── schema.sql ← source of truth; edit this to change the db
├── migrations/ ← versioned .sql files for post-deploy changes
│ └── 001_initial.sql
├── scripts/
│ ├── init_db.py ← bootstrap + sync; run after every schema change
│ └── db_cli.py ← template; init_db.py bakes DB_PATH and copies it
├── db/
│ └── <name>.db ← DB lives here (add to .gitignore)
└── .claude/skills/<name>-db/
├── SKILL.md ← auto-generated agent skill (schema + commands)
└── db_cli.py ← baked copy with absolute DB_PATH
Global scope — personal DB usable from any project
Everything lives together in one folder; nothing is written to the current project directory.
~/db/<name>/
├── <name>.db ← the database
├── schema.sql ← source of truth
├── init_db.py ← run from anywhere: python3 ~/db/<name>/init_db.py
├── db_cli.py ← template; init_db.py bakes DB_PATH and copies it
└── migrations/
└── 001_initial.sql
~/.claude/skills/<name>-db/
├── SKILL.md ← auto-generated agent skill (schema + commands)
└── db_cli.py ← baked copy with absolute DB_PATH
init_db.py copies db_cli.py into the skill folder at first run with DB_PATH hardcoded
to the absolute path — so the agent can call it from any working directory.
Scope options (set at the top of init_db.py):
DB_SCOPE | DB + artifacts location | Skill location | Use when |
|---|---|---|---|
local (default) | <project>/db/<name>.db + scripts/, migrations/ in project root | .claude/skills/<name>-db/ | DB belongs to this project |
global | ~/db/<name>/ (DB, schema, migrations, scripts all here) | ~/.claude/skills/<name>-db/ | Personal DB used across projects |
Phase 1 — Brainstorm Before Writing SQL
Never open a text editor until this phase is complete. Ask the user these questions one group at a time, waiting for answers before moving on. Use their answers to catch design problems early — it is much cheaper to rename an entity now than to migrate data later.
1.1 — Goals and scope
Ask:
- What are you trying to achieve with this database? What problem does it solve?
- Who or what reads from it — a human via CLI, an agent, a web app, or all three?
- Roughly how much data do you expect — hundreds of rows, millions, or somewhere in between?
- Does this need to be shared across machines, or is local-only fine?
- Will other skills or tools need to read from this db? If so, what do they need to know about it?
Listen for: scale hints (affects index strategy), sharing needs (flag if they describe
multi-writer or networked access — SQLite may be the wrong choice), and interop signals
(another skill consuming this db means the context/schema commands are essential).
1.2 — Major entities
Ask:
- What are the main "things" your app tracks? (e.g. users, orders, products, sessions, events)
- For each thing: what's the natural identifier — a name people type, an auto-generated ID, or something from an external system (like an order number from an API)?
- Are any of these things hierarchical? (e.g. "a cart has items")
From their answers, draft a plain-English entity list like:
Entities identified:
- Order — one per placed order; identified by auto-increment ID
- Template — named reusable config; identified by a human-chosen name
- OrderItem — child of Order; one row per line item
Show this to the user and confirm before continuing.
1.3 — Relationships
Ask:
- Which entities belong to other entities? (one order has many items, one user has many orders)
- Can something belong to multiple parents? (a product can be in many orders — many-to-many)
- When a parent is deleted, what should happen to its children?
- Cascade delete (children disappear too)?
- Restrict (block the delete if children exist)?
- Set null (children become orphaned but stay)?
Map this to foreign keys:
OrderItem.order_id → Order.id ON DELETE CASCADE
Flag many-to-many relationships early — they need a join table and the user may not realize it.
1.4 — Column special cases
For each entity, ask:
Full-text search
- Do users need to search this entity by keyword? (e.g. "find orders mentioning pepperoni")
- If yes → plan an FTS5 virtual table + sync trigger for that entity (see Column Patterns below).
JSON / flexible payloads
- Are there fields where the shape varies per row, or you don't know all the keys yet?
- If yes →
TEXTcolumn storing JSON. Name itpayload,metadata, orconfig(notdata— too vague). Always note in the comment what keys you expect:-- JSON: {store_id, items[], note}.
Enumerations
- Are there status or type fields with a fixed set of values?
- If yes →
TEXTwith aCHECKconstraint:CHECK(status IN ('pending','active','done')). Don't use integers for enums in SQLite — unreadable in queries.
Vectors
- Do you need semantic similarity search (find records "like" this one)?
- If yes → plan a
vec0virtual table viasqlite-vec. Requirespip install sqlite-vecand storing embeddings separately from the main table (see Column Patterns below).
Soft deletes
- Will any other skill or tool need to see historical records even after they're "removed"?
- Will you need an audit trail or undo capability?
- If yes (or if unsure) → add
deleted_at TEXT DEFAULT NULLto the table. Rows are never hard-deleted; insteadUPDATE t SET deleted_at = datetime('now') WHERE id = ?. All read commands filterWHERE deleted_at IS NULLby default; passinclude_deleted=trueto see all.
1.5 — Query patterns
Ask:
- What are the three most common queries you'll run? (e.g. "get all pending orders", "find a template by name", "order history for a customer")
- Do you need to filter or sort by date often?
- Do you need to look things up by a non-primary-key field frequently? (e.g. "find order by phone number" — that column needs an index)
- Will you ever need to run ad-hoc queries not covered by named commands?
(if yes → the
queryescape hatch in db_cli.py handles this safely)
Use answers to plan indexes beyond the primary key.
1.6 — Storage scope
Ask:
- Will this DB be used only inside this project, or do you want it accessible from any project (or by a webapp running independently)?
Local (DB_SCOPE=local, default):
- DB at
./db/<name>.db— travels with the project, easy to gitignore - Skill at
.claude/skills/<name>-db/— only visible inside this project - Best for: a DB that belongs to a specific codebase
Global (DB_SCOPE=global):
- Everything at
~/db/<name>/— DB, schema.sql, migrations/, init_db.py all in one folder; nothing written to the project directory - Skill at
~/.claude/skills/<name>-db/— visible from every project - Best for: personal tools (second brain, bug tracker, link vault)
Webapp access: In both scopes, webapps connect directly to the .db file path —
no API layer needed. The generated skill documents the path and driver snippets.
Flag if they say: "share it", "export it", "download it" — use db-export command
in db_cli.py which copies the .db file itself. Add present_files call if available.
1.7 — Design confirmation
Before writing any SQL, present a summary:
Proposed design:
Tables: orders, order_items, templates
Soft deletes: orders (deleted_at column)
FTS: orders_fts (on customer, item fields)
Indexes: orders(status), orders(created_at), order_items(order_id)
JSON cols: templates.payload {store_id, items[], payment_ref}
Enums: orders.status IN ('pending','confirmed','delivered','cancelled')
DB path: ./myapp.db (project-local, added to .gitignore)
Migrations: migrations/ folder, versioned from 001
Questions / flags:
- order_items is a new table not mentioned initially — confirm this is right
- Soft deletes on orders only; order_items cascade-delete so no need there
- No vector search planned; add later if needed
Get explicit confirmation before moving to Phase 2.
Phase 2 — Write schema.sql
With the design confirmed, translate it into SQL. Follow all rules below.
Mandatory columns on every table
created_at TEXT NOT NULL DEFAULT (datetime('now')), -- set once on insert, never updated
updated_at TEXT NOT NULL DEFAULT (datetime('now')), -- updated via trigger on every UPDATE
deleted_at TEXT DEFAULT NULL -- NULL = live; timestamp = soft-deleted
Always add updated_at and soft-delete triggers for every table:
CREATE TRIGGER orders_updated_at
AFTER UPDATE ON orders
BEGIN
UPDATE orders SET updated_at = datetime('now') WHERE id = NEW.id;
END;
Column patterns
Standard row (append-only records):
CREATE TABLE IF NOT EXISTS orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
deleted_at TEXT DEFAULT NULL, -- soft delete
customer TEXT NOT NULL, -- display name or user ID
status TEXT NOT NULL DEFAULT 'pending'
CHECK(status IN ('pending','confirmed','delivered','cancelled')),
total_cents INTEGER NOT NULL -- price in cents, never floats
);
Named/keyed resource (upsertable):
CREATE TABLE IF NOT EXISTS templates (
name TEXT PRIMARY KEY, -- human key, e.g. "friday-night"
payload TEXT NOT NULL, -- JSON: {store_id, items[], payment_ref}
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
deleted_at TEXT DEFAULT NULL
);
Child table (foreign key):
CREATE TABLE IF NOT EXISTS order_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_id INTEGER NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
product_code TEXT NOT NULL, -- e.g. "S_PIZPH"
quantity INTEGER NOT NULL DEFAULT 1,
unit_cents INTEGER NOT NULL
);
Full-text search (FTS5 virtual table + sync triggers):
CREATE VIRTUAL TABLE IF NOT EXISTS orders_fts USING fts5(
customer,
product_code,
content='orders',
content_rowid='id'
);
CREATE TRIGGER orders_fts_ai AFTER INSERT ON orders BEGIN
INSERT INTO orders_fts(rowid, customer, product_code)
VALUES (new.id, new.customer, new.product_code);
END;
CREATE TRIGGER orders_fts_ad AFTER DELETE ON orders BEGIN
INSERT INTO orders_fts(orders_fts, rowid, customer, product_code)
VALUES ('delete', old.id, old.customer, old.product_code);
END;
Vector search (sqlite-vec, installed separately):
-- Requires: pip install sqlite-vec
CREATE VIRTUAL TABLE IF NOT EXISTS order_embeddings USING vec0(
id INTEGER PRIMARY KEY,
vector FLOAT[1536]
);
Index patterns
CREATE INDEX IF NOT EXISTS idx_orders_created_at ON orders(created_at);
CREATE INDEX IF NOT EXISTS idx_orders_updated_at ON orders(updated_at);
CREATE INDEX IF NOT EXISTS idx_orders_deleted_at ON orders(deleted_at); -- fast live-row filter
CREATE INDEX IF NOT EXISTS idx_orders_status ON orders(status);
CREATE INDEX IF NOT EXISTS idx_orders_status_date ON orders(status, created_at);
CREATE INDEX IF NOT EXISTS idx_order_items_order ON order_items(order_id);
Rule of thumb: if a query filters or sorts by a column more than occasionally, it needs an index.
deleted_at always gets an index — nearly every query filters on it.
Phase 3 — Migrations
Schema changes after the initial deploy go in migrations/, not back into schema.sql directly.
This preserves history and lets init_db.py apply only the deltas that haven't run yet.
Migration file naming
migrations/
001_initial.sql ← copy of the original schema.sql at deploy time
002_add_phone.sql ← ALTER TABLE orders ADD COLUMN phone TEXT;
003_add_notes.sql ← ALTER TABLE orders ADD COLUMN notes TEXT DEFAULT '';
Each file is append-only — never edit a migration that has already run.
Running the sync
python3 init_db.py # global scope
python3 scripts/init_db.py # local scope
This does three things in one shot:
- Creates the
_migrationstracking table if it doesn't exist. - Applies pending
migrations/*.sqlfiles in order. - Rewrites the Schema Reference section of the generated
SKILL.mdwith the live column structure.
Self-check after every migration — REQUIRED
init_db.py updates the DB and the Schema Reference. It does not update db_cli.py or schema.sql.
After every migration you MUST do all of the following, then re-run init_db.py to rebake the skill copy.
Always — update schema.sql to match the new state:
schema.sql is the source of truth for the complete current schema. Every migration must be reflected back into it so the file always represents what a fresh deploy would look like.
- For every
ALTER TABLE ... ADD COLUMN: add the column to the matchingCREATE TABLEblock inschema.sql - For every column removed or renamed: update the
CREATE TABLEblock inschema.sqlaccordingly - For every new table: add the full
CREATE TABLE+ triggers + indexes toschema.sql
Work through this checklist for db_cli.py:
For every new column added:
-
cmd_insert_<entity>— add the column as an optional arg; include it in the INSERT cols/vals if present -
cmd_update_<entity>— addif "col" in args: fields["col"] = args["col"]and update the error message listing updatable fields -
cmd_list_<entity>— if the column is filterable (status, type, badge number), add aif col: conditions.append(...)branch -
cmd_get_<entity>— usually no change needed (usesSELECT *) - Returned JSON in insert/update — include the new column in the
ok({...})response so callers see it
For every column removed or renamed:
- Remove or rename all references in insert, update, list, and any named-query commands
- Check
bulk-import/bulk-export— they use column names from the caller; document the change
For every new table added:
- Add a full set of CRUD commands:
insert-<entity>,get-<entity>,list-<entity>,update-<entity> - Add the table to
cmd_contextif it needs a live/total row count - Add the table name to the
COMMANDSdispatch dict - Update the
## Command Referencetable in the generated SKILL.md
After updating db_cli.py, re-run init_db.py to copy the updated template into the skill folder:
python3 init_db.py # rebakes ~/.claude/skills/<name>-db/db_cli.py
Then verify end-to-end with a quick smoke test:
python3 ~/.claude/skills/<name>-db/db_cli.py insert-<entity> '{"new_col":"value",...}'
python3 ~/.claude/skills/<name>-db/db_cli.py update-<entity> '{"id":1,"new_col":"value"}'
Phase 4 — db_cli.py
The full command surface lives in scripts/db_cli.py — bundled with this skill. Other skills
call it as a subprocess and parse the JSON output. Every command outputs JSON on stdout;
errors go to stderr with a non-zero exit code.
Agent call examples:
python3 scripts/db_cli.py context '{}'
python3 scripts/db_cli.py schema '{}'
python3 scripts/db_cli.py insert-order '{"customer":"aly","total_cents":1299}'
python3 scripts/db_cli.py recent-orders '{"limit":5,"status":"pending","since":"2026-01-01"}'
python3 scripts/db_cli.py fts-search '{"query":"pepperoni","limit":5}'
python3 scripts/db_cli.py save-template '{"name":"friday-night","payload":{"store":7144}}'
python3 scripts/db_cli.py get-template '{"name":"friday-night"}'
python3 scripts/db_cli.py update-order-status '{"order_id":3,"status":"delivered"}'
python3 scripts/db_cli.py soft-delete '{"table":"orders","record_id":3}'
python3 scripts/db_cli.py restore '{"table":"orders","record_id":3}'
python3 scripts/db_cli.py query '{"sql":"SELECT * FROM orders WHERE status = ?","params":["pending"]}'
python3 scripts/db_cli.py bulk-import '{"table":"orders","records":[...],"mode":"upsert"}'
python3 scripts/db_cli.py bulk-export '{"table":"orders","fmt":"csv","dest":"orders.csv"}'
python3 scripts/db_cli.py db-export '{}'
Command Reference
Interop commands (for other skills)
| Command | Args | Returns |
|---|---|---|
context | — | {db_path, exists, tables: {name: {live_rows, total_rows}}, commands[]} |
schema | — | {tables: {name: [{name,type,not_null,default,pk}]}, indexes[], migrations[]} |
How another skill consumes this db:
# 1. Discover what's here
python3 db_cli.py context '{}'
# 2. Get full column detail for a specific table
python3 db_cli.py schema '{}' | python3 -c "import sys,json; s=json.load(sys.stdin); print(json.dumps(s['tables']['orders'], indent=2))"
# 3. Run a targeted read
python3 db_cli.py recent-orders '{"limit":10,"status":"pending"}'
Write commands
| Command | Key args | Notes |
|---|---|---|
insert-order | customer, total_cents | Returns {id} |
save-template | name, payload (dict) | Upsert; restores if soft-deleted |
update-order-status | order_id, status | |
soft-delete | table, record_id | Sets deleted_at; never hard-deletes |
restore | table, record_id | Clears deleted_at |
Read commands
| Command | Key args | Notes |
|---|---|---|
get-order | order_id | Pass include_deleted=true to see soft-deleted |
get-template | name | Returns null if not found or soft-deleted |
recent-orders | limit, status, since, include_deleted | since uses index |
fts-search | query, limit | Returns [{rowid, rank}] |
query | sql, params | SELECT only; raises on any write keyword |
Bulk commands
| Command | Key args | Notes |
|---|---|---|
bulk-import | table, records (list), mode | insert / upsert / ignore |
bulk-export | table, fmt, dest | json or csv; omit dest for inline |
db-export | dest | Copies the .db file itself |
Schema Reference
<!-- AUTO-GENERATED — edit schema.sql or add a migration, then run python3 init_db.py -->Run python3 init_db.py once to populate this section with your live schema.
DB Path Convention
| Option | Path | When to use |
|---|---|---|
| Project-local | ./<projectname>.db | Code and db travel together. Add to .gitignore. |
| User data dir | ~/.local/share/<appname>/store.db | Persistent user state; survives project moves. |
| Temp/scratch | /tmp/<name>.db | Throwaway experiments. Wiped on reboot. |
| Download | current dir + present_files | One-off export or sandboxed environments. |
| Custom | user-specified path | Anything else. |
DB_PATH is set once in both init_db.py and db_cli.py — change it in one place during
Phase 1 so it never drifts between the two files.
The Sync Loop
Edit schema.sql (or add migrations/<N>_change.sql for existing dbs)
↓
python3 scripts/init_db.py
↓
db/<name>.db updated
.claude/skills/<name>-db/SKILL.md ← Schema Reference section spliced
.claude/skills/<name>-db/db_cli.py ← re-copied with absolute DB_PATH
↓
Agent loads <name>-db skill → knows exact tables/columns/indexes
↓
Other skills call: python3 .claude/skills/<name>-db/db_cli.py context '{}'
Webapps connect to: db/<name>.db directly
No manual skill edits. No schema inspection at runtime. One command keeps everything in sync.
Phase 5 — The Generated Skill
After the first python3 scripts/init_db.py run, a self-contained skill is written to
.claude/skills/<name>-db/ (or ~/.claude/skills/<name>-db/ for global scope).
What it contains:
- Frontmatter
nameanddescriptiontailored to the DB's entity names so it activates on phrases like "add a bug", "list orders", "search bugtracker" - DB path and CLI reference (absolute, works from any directory)
- Schema Reference — the only section that changes after migrations
- Full command reference with entity-specific examples
- Webapp access snippets (Python / Node / Bun+Drizzle)
- Migration instructions
What gets re-generated on every init_db.py run:
db_cli.pyin the skill folder (in case source changed)- The
## Schema Referencesection inSKILL.mdonly — everything else is stable
What to set before generating:
DB_SCOPE—local(default) orglobalDB_NAME— defaults to the current directory name; override if different- Entity names in the description —
init_db.pyderives them from the first table name; edit the generated skill's frontmatterdescriptionif the auto-derived name is wrong (e.g. "goose" → "geese", "person" → "people")
Gives 0 of the 12 instructions most databases sql skills give in ~5.5k tokens
Counted across 589 of the 662 authors here whose files we hold, read 2026-08-06
- use parameterized queriesin 36 of 589, across 32 files
- use timestamptz for timestampsin 30 of 589, across 12 files
- create indexes concurrentlyin 29 of 589, across 23 files
- index foreign keysin 28 of 589, across 17 files
- use numeric type for moneyin 25 of 589, across 8 files
- select only required columnsin 24 of 589, across 19 files
- use cursor pagination instead of OFFSETin 23 of 589, across 15 files
- add indexes manually on foreign key columnsin 22 of 589, across 11 files
- read individual rule files for detailed explanationsin 18 of 589, across 4 files
- configure connection poolingin 18 of 589, across 16 files
- put equality columns before range columns in indexesin 17 of 589, across 9 files
- normalize to third normal formin 17 of 589, across 8 files
Said here and by no other author read
- complete a brainstorming phase before writing sql
- confirm the proposed design before writing sql
- treat schema.sql as the single source of truth
- add updated_at and soft-delete triggers for every table
- use text with check constraints for enumerations
- run init_db.py after every schema change
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.