Internal SmartGift build of a Claude Code monitoring dashboard. Lanes: a durable unit of parallel agent work, one per working directory, tracked across session restarts. Managed lanes are git worktrees the dashboard provisions and can reset or remove behind a three-check destroy guard and a counted preflight; adopted lanes are directories you already own and are never destroyable. Pipelines: a lane moves through pipeline stages. A stage the agent declares with evidence renders green; a stage inferred from the tool-event stream renders dashed amber and never counts as done. Detection is forward-only within a 30-minute window, and never writes the declared stage. Workspace: one page at /run with a lane grid, the selected lane's pipeline, and a full Claude console behind a disclosure.
7.2 KiB
Remove Native SQLite Dependency — Implementation Plan
For agentic workers: REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Replace better-sqlite3 (native C++ module requiring Python/build tools) with a compatibility layer over Node.js built-in node:sqlite, so npm install succeeds on any machine without native compilation tools.
Architecture: Create server/compat-sqlite.js — a thin wrapper that gives DatabaseSync (from node:sqlite) the same API as better-sqlite3. Move better-sqlite3 to optionalDependencies so it's preferred when prebuilds are available but doesn't block install. The server/db.js loader tries better-sqlite3 first, falls back to the compat wrapper. Update minimum Node version to 22.
Tech Stack: Node.js node:sqlite (DatabaseSync), existing Express/WS server
File Structure
| File | Action | Responsibility |
|---|---|---|
server/compat-sqlite.js |
Create | Wrapper class: DatabaseSync → better-sqlite3 API |
server/db.js |
Modify (line 1) | Try better-sqlite3, fallback to compat wrapper |
scripts/clear-data.js |
Modify (line 8) | Same fallback import |
package.json |
Modify | Move better-sqlite3 to optionalDependencies, bump engines to >=22 |
server/__tests__/api.test.js |
Modify (lines 952-959) | Fix db.pragma() calls to work with both backends |
Chunk 1: Core Implementation
Task 1: Create server/compat-sqlite.js
Files:
-
Create:
server/compat-sqlite.js -
Step 1: Write the compat wrapper
The wrapper must bridge these API differences:
| better-sqlite3 | node:sqlite (DatabaseSync) |
|---|---|
new Database(path) |
new DatabaseSync(path) |
db.pragma("key = value") |
db.exec("PRAGMA key = value") |
db.pragma("key") → value |
db.prepare("PRAGMA key").get() → {key: value} |
db.pragma("key", { simple: true }) → value |
same as above, extract single value |
db.transaction(fn) → wrapper fn |
manual BEGIN/COMMIT/ROLLBACK |
db.prepare(sql) → stmt with .run(), .get(), .all() |
identical API |
db.exec(sql) |
identical |
db.close() |
identical |
// server/compat-sqlite.js
const { DatabaseSync } = require("node:sqlite");
class Database {
constructor(filePath) {
this._db = new DatabaseSync(filePath);
}
exec(sql) {
this._db.exec(sql);
return this;
}
pragma(str, options) {
if (str.includes("=")) {
this._db.exec(`PRAGMA ${str}`);
return undefined;
}
const row = this._db.prepare(`PRAGMA ${str}`).get();
if (!row) return undefined;
const keys = Object.keys(row);
if (options?.simple || keys.length === 1) return row[keys[0]];
return row;
}
prepare(sql) {
return this._db.prepare(sql);
}
transaction(fn) {
const db = this._db;
const wrapper = (...args) => {
db.exec("BEGIN");
try {
const result = fn(...args);
db.exec("COMMIT");
return result;
} catch (err) {
db.exec("ROLLBACK");
throw err;
}
};
return wrapper;
}
close() {
this._db.close();
}
}
module.exports = Database;
- Step 2: Verify the wrapper works standalone
Run: node -e "const DB = require('./server/compat-sqlite'); const db = new DB(':memory:'); db.pragma('journal_mode = WAL'); db.exec('CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT)'); const s = db.prepare('INSERT INTO t (v) VALUES (?)'); console.log(s.run('hi')); console.log(db.prepare('SELECT * FROM t').all()); const tx = db.transaction((items) => { for (const i of items) s.run(i); }); tx(['a','b','c']); console.log(db.prepare('SELECT COUNT(*) as c FROM t').get()); db.close(); console.log('OK')"
Expected: OK printed at the end with correct query results.
- Step 3: Commit
git add server/compat-sqlite.js
git commit -m "feat: add node:sqlite compat wrapper for better-sqlite3 API"
Task 2: Update server/db.js to use fallback import
Files:
-
Modify:
server/db.js:1 -
Step 1: Replace the import
Change line 1 from:
const Database = require("better-sqlite3");
To:
let Database;
try {
Database = require("better-sqlite3");
} catch {
Database = require("./compat-sqlite");
}
- Step 2: Verify server starts
Run: node -e "process.env.DASHBOARD_DB_PATH = require('path').join(require('os').tmpdir(), 'test-fallback-' + Date.now() + '.db'); const { db, stmts } = require('./server/db'); console.log('stmts keys:', Object.keys(stmts).length); stmts.insertSession.run('test-1', 'Test', 'active', null, null, null); console.log(stmts.getSession.get('test-1')); db.close(); console.log('OK')"
Expected: Prints statement count (39), session row, and OK.
- Step 3: Commit
git add server/db.js
git commit -m "feat: fallback to node:sqlite when better-sqlite3 unavailable"
Task 3: Update scripts/clear-data.js to use fallback import
Files:
-
Modify:
scripts/clear-data.js:8 -
Step 1: Replace the import
Change line 8 from:
const Database = require("better-sqlite3");
To:
let Database;
try {
Database = require("better-sqlite3");
} catch {
Database = require("../server/compat-sqlite");
}
- Step 2: Commit
git add scripts/clear-data.js
git commit -m "fix: use fallback sqlite import in clear-data script"
Task 4: Update package.json
Files:
-
Modify:
package.json -
Step 1: Move better-sqlite3 to optionalDependencies, bump engines
Move "better-sqlite3": "^11.7.0" from dependencies to optionalDependencies.
Change engines from "node": ">=18.0.0" to "node": ">=22.0.0".
- Step 2: Commit
git add package.json
git commit -m "chore: make better-sqlite3 optional, require Node >= 22"
Task 5: Fix test pragma calls
Files:
-
Modify:
server/__tests__/api.test.js:952-959 -
Step 1: Fix pragma calls in Database Integrity tests
The tests call db.pragma("journal_mode", { simple: true }) and db.pragma("foreign_keys", { simple: true }). The compat wrapper supports { simple: true }, so these should work as-is. However, WAL mode isn't available for in-memory databases (returns "memory"). The test creates a file-based DB via TEST_DB, so WAL should work.
No change needed — verify by running tests.
- Step 2: Run full test suite
Run: node --test server/__tests__/api.test.js
Expected: All tests pass.
- Step 3: Run setup to verify npm install succeeds without Python
Run: npm run setup
Expected: Install succeeds (better-sqlite3 may warn but won't fail since it's optional).
Task 6: Update documentation
Files:
-
Modify:
SETUP.md(if it mentions better-sqlite3 or Python requirements) -
Step 1: Check and update SETUP.md
Remove any mentions of Python or build tools as requirements. Note that Node >= 22 is required.
- Step 2: Commit all remaining changes
git add -A
git commit -m "docs: update setup requirements for native-free SQLite"