Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use when a val needs to store structured or relational data. Covers the std/sqlite API, parameterized queries, transactions, and the val-scoped vs organization-scoped database distinction.
.claude/skills/hashgraph-online-sqlite-storage/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-12 | ✗→✓ | ▲ Improved | -24% | 0% |
| case-10 | ✗→✓ | ▲ Improved | -15% | 0% |
| case-01 | ✗→✓ | ▲ Improved | -21% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 10% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 11% | 0% |
Val Town provides built-in SQLite via the std/sqlite module. Reach for it whenever a val needs relational or structured persistent data. For simple key/value data, prefer std/blob instead.
tsimport { sqlite } from "https://esm.town/v/std/sqlite/main.ts"; await sqlite.execute(`CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE )`); await sqlite.execute({ sql: "INSERT INTO users (name, email) VALUES (?, ?)", args: ["Alice", "alice@example.com"], }); const result = await sqlite.execute("SELECT * FROM users"); // result.rows = [{ id: 1, name: "Alice", email: "alice@example.com" }]
Use sqlite.batch for atomic multi-statement transactions — all succeed or all roll back:
tsawait sqlite.batch([ { sql: "INSERT INTO users (name, email) VALUES (?, ?)", args: ["Bob", "bob@example.com"] }, { sql: "UPDATE users SET name = ? WHERE id = ?", args: ["Robert", 2] }, ]);
The import path determines which database you get. Both expose the same @libsql/client API (execute, batch) and return rows as keyed objects (Record<string, unknown>[]):
std/sqlite/main.ts — val-scoped database, isolated to this val. The default for new vals, and what you almost always want.std/sqlite/global.ts — organization-scoped database, shared across every val owned by the same account. (Your personal account counts as its own organization here, so this database is shared across all of your vals.)Do not switch an existing val between these import paths — it changes which database the val reads and writes.
When using the sqlite_execute or sqlite_batch tools to query a val owned by an organization (not your personal account), pass the org handle as the org parameter so the call hits the right database. Example: { sql: "SELECT * FROM users", org: "some-org" }. This only matters for the tool calls — code inside the val itself reads from its own database automatically.
args field) for any value derived from user input. Never interpolate strings into SQL.CREATE TABLE IF NOT EXISTS so schema setup is idempotent across val restarts.ALTER TABLE ... ADD COLUMN. Wrap in try/catch if the migration may run against an already-updated table.Full API docs: https://docs.val.town/reference/std/sqlite/usage/
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-12 | fail→pass | 18,719 | 11,876 | -37% | 1 | 1 | 0% | 2,106 | 1,608 | -24% | 0 | 0 | — |
case-10 | fail→pass | 14,920 | 5,992 | -60% | 1 | 1 | 0% | 1,814 | 1,542 | -15% | 0 | 0 | — |
case-01 | fail→pass | 12,091 | 10,811 | -11% | 1 | 1 | 0% | 2,244 | 1,779 | -21% | 0 | 0 | — |
case-02 | fail→pass | 9,183 | 9,682 | +5% | 1 | 1 | 0% | 2,127 | 2,341 | +10% | 0 | 0 | — |
case-03 | fail→pass | 17,683 | 10,442 | -41% | 1 | 1 | 0% | 2,459 | 2,741 | +11% | 0 | 0 | — |
case-04 | fail→pass | 13,568 | 3,791 | -72% | 1 | 1 | 0% | 1,356 | 992 | -27% | 0 | 0 | — |
case-05 | fail→pass | 6,838 | 8,200 | +20% | 1 | 1 | 0% | 1,073 | 1,231 | +15% | 0 | 0 | — |
case-11 | pass→pass | 14,433 | 9,465 | -34% | 1 | 1 | 0% | 1,470 | 1,135 | -23% | 0 | 0 | — |
case-06 | pass→pass | 31,871 | 10,868 | -66% | 1 | 1 | 0% | 1,618 | 1,418 | -12% | 0 | 0 | — |
case-07 | pass→pass | 11,873 | 9,957 | -16% | 1 | 1 | 0% | 2,149 | 1,488 | -31% | 0 | 0 | — |
case-08 | pass→pass | 13,443 | 9,957 | -26% | 1 | 1 | 0% | 1,498 | 1,583 | +6% | 0 | 0 | — |
case-09 | pass→pass | 15,395 | 12,374 | -20% | 1 | 1 | 0% | 2,618 | 1,994 | -24% | 0 | 0 | — |
case-13 | pass→pass | 13,648 | 8,648 | -37% | 1 | 1 | 0% | 1,450 | 1,289 | -11% | 0 | 0 | — |
case-14 | pass→pass | 12,925 | 3,395 | -74% | 1 | 1 | 0% | 1,271 | 1,165 | -8% | 0 | 0 | — |
case-15 | pass→pass | 4,305 | 7,852 | +82% | 1 | 1 | 0% | 749 | 1,043 | +39% | 0 | 0 | — |
case-16 | pass→pass | 18,440 | 14,022 | -24% | 1 | 1 | 0% | 1,945 | 2,359 | +21% | 0 | 0 | — |
case-17 | fail→pass | 16,310 | 11,098 | -32% | 1 | 1 | 0% | 2,274 | 1,689 | -26% | 0 | 0 | — |
case-18 | pass→pass | 18,862 | 2,966 | -84% | 1 | 1 | 0% | 1,052 | 995 | -5% | 0 | 0 | — |
case-19 | pass→pass | 6,028 | 34,955 | +480% | 1 | 1 | 0% | 1,005 | 1,322 | +32% | 0 | 0 | — |
case-20 | pass→pass | 12,240 | 3,736 | -69% | 1 | 1 | 0% | 1,244 | 1,258 | +1% | 0 | 0 | — |
case-21 | pass→pass | 9,742 | 15,469 | +59% | 1 | 1 | 0% | 1,854 | 2,597 | +40% | 0 | 0 | — |
case-22 | pass→pass | 11,944 | 10,414 | -13% | 1 | 1 | 0% | 2,175 | 2,011 | -8% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +36 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.