feat(KB-627): Steps 1-2 — Create ModelSelectionModal and integrate with QuickEntryBox

This commit is contained in:
gsxdsm
2026-03-31 17:51:20 -07:00
parent dbfb8bc1d9
commit be5a7d9b3f
5 changed files with 405 additions and 95 deletions

View File

@@ -57,7 +57,7 @@ export function fromJson<T>(json: string | null | undefined): T | undefined {
// ── Schema Definition ────────────────────────────────────────────────
const SCHEMA_VERSION = 1;
const SCHEMA_VERSION = 2;
const SCHEMA_SQL = `
-- Tasks table with JSON columns for nested data
@@ -219,12 +219,15 @@ export class Database {
// Seed schemaVersion and lastModified idempotently
this.db.exec(
`INSERT OR IGNORE INTO __meta (key, value) VALUES ('schemaVersion', '${SCHEMA_VERSION}')`,
`INSERT OR IGNORE INTO __meta (key, value) VALUES ('schemaVersion', '1')`,
);
this.db.exec(
`INSERT OR IGNORE INTO __meta (key, value) VALUES ('lastModified', '${Date.now()}')`,
);
// Run schema migrations
this.migrate();
// Seed config row idempotently
const configNow = new Date().toISOString();
this.db.exec(
@@ -232,6 +235,64 @@ export class Database {
);
}
/**
* Run incremental schema migrations based on the stored schema version.
*
* Each migration block is guarded by a version check and runs inside a
* transaction so that a failed migration leaves the database unchanged.
* New migrations should be added as `if (version < N)` blocks before
* the final version bump, and SCHEMA_VERSION should be incremented to N.
*
* Column additions use `hasColumn()` so they are idempotent — safe to
* re-run even if a previous migration partially applied.
*/
private migrate(): void {
const version = this.getSchemaVersion() || 1;
if (version >= SCHEMA_VERSION) return;
if (version < 2) {
this.applyMigration(2, () => {
this.addColumnIfMissing("tasks", "comments", "TEXT DEFAULT '[]'");
this.addColumnIfMissing("tasks", "mergeDetails", "TEXT");
});
}
// Future migrations go here:
// if (version < 3) { this.applyMigration(3, () => { ... }); }
}
/**
* Run a single migration step inside a transaction and bump the version.
*/
private applyMigration(targetVersion: number, fn: () => void): void {
// SQLite ALTER TABLE cannot run inside a transaction, so we run the
// migration function directly and only bump the version on success.
fn();
this.db
.prepare("UPDATE __meta SET value = ? WHERE key = 'schemaVersion'")
.run(String(targetVersion));
}
/**
* Check whether a table has a given column.
*/
private hasColumn(table: string, column: string): boolean {
const cols = this.db
.prepare(`PRAGMA table_info(${table})`)
.all() as Array<{ name: string }>;
return cols.some((c) => c.name === column);
}
/**
* Add a column to a table if it does not already exist.
*/
private addColumnIfMissing(table: string, column: string, definition: string): void {
if (!this.hasColumn(table, column)) {
this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
}
}
/**
* Close the database connection.
*/