From 24c84088e65570214f5d9f868ac7a299d67a6d6d Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 16 Feb 2026 12:48:44 -0500 Subject: [PATCH 1/2] feat(cli): add db migrate command for JSON to SQLite migration --- packages/opencode/src/cli/cmd/db.ts | 52 ++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/db.ts b/packages/opencode/src/cli/cmd/db.ts index 0ade4d3c4bf8..30f44a9b0df2 100644 --- a/packages/opencode/src/cli/cmd/db.ts +++ b/packages/opencode/src/cli/cmd/db.ts @@ -4,6 +4,8 @@ import { Database } from "../../storage/db" import { Database as BunDatabase } from "bun:sqlite" import { UI } from "../ui" import { cmd } from "./cmd" +import { JsonMigration } from "../../storage/json-migration" +import { EOL } from "os" const QueryCommand = cmd({ command: "$0 [query]", @@ -58,11 +60,59 @@ const PathCommand = cmd({ }, }) +const MigrateCommand = cmd({ + command: "migrate", + describe: "migrate JSON data to SQLite (merges with existing data)", + handler: async () => { + const sqlite = new BunDatabase(Database.Path) + const tty = process.stderr.isTTY + const width = 36 + const orange = "\x1b[38;5;214m" + const muted = "\x1b[0;2m" + const reset = "\x1b[0m" + let last = -1 + if (tty) process.stderr.write("\x1b[?25l") + try { + const stats = await JsonMigration.run(sqlite, { + progress: (event) => { + const percent = Math.floor((event.current / event.total) * 100) + if (percent === last && event.current !== event.total) return + last = percent + if (tty) { + const fill = Math.round((percent / 100) * width) + const bar = `${"■".repeat(fill)}${"・".repeat(width - fill)}` + process.stderr.write( + `\r${orange}${bar} ${percent.toString().padStart(3)}%${reset} ${muted}${event.label.padEnd(12)} ${event.current}/${event.total}${reset}`, + ) + if (event.current === event.total) process.stderr.write("\n") + } else { + process.stderr.write(`sqlite-migration:${percent}${EOL}`) + } + }, + }) + if (tty) process.stderr.write("\x1b[?25h") + else process.stderr.write(`sqlite-migration:done${EOL}`) + UI.println( + `Migration complete: ${stats.projects} projects, ${stats.sessions} sessions, ${stats.messages} messages`, + ) + if (stats.errors.length > 0) { + UI.println(`${stats.errors.length} errors occurred during migration`) + } + } catch (err) { + if (tty) process.stderr.write("\x1b[?25h") + UI.error(`Migration failed: ${err instanceof Error ? err.message : String(err)}`) + process.exit(1) + } finally { + sqlite.close() + } + }, +}) + export const DbCommand = cmd({ command: "db", describe: "database tools", builder: (yargs: Argv) => { - return yargs.command(QueryCommand).command(PathCommand).demandCommand() + return yargs.command(QueryCommand).command(PathCommand).command(MigrateCommand).demandCommand() }, handler: () => {}, }) From df6051a09ea213b06b1d21ffdee6fdbeaef0a19a Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 16 Feb 2026 13:59:57 -0500 Subject: [PATCH 2/2] tui: fix migration progress bar to show final 100% state and improve label positioning --- packages/opencode/src/cli/cmd/db.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/cli/cmd/db.ts b/packages/opencode/src/cli/cmd/db.ts index 30f44a9b0df2..8ca4b9a42eb2 100644 --- a/packages/opencode/src/cli/cmd/db.ts +++ b/packages/opencode/src/cli/cmd/db.ts @@ -76,20 +76,20 @@ const MigrateCommand = cmd({ const stats = await JsonMigration.run(sqlite, { progress: (event) => { const percent = Math.floor((event.current / event.total) * 100) - if (percent === last && event.current !== event.total) return + if (percent === last) return last = percent if (tty) { const fill = Math.round((percent / 100) * width) const bar = `${"■".repeat(fill)}${"・".repeat(width - fill)}` process.stderr.write( - `\r${orange}${bar} ${percent.toString().padStart(3)}%${reset} ${muted}${event.label.padEnd(12)} ${event.current}/${event.total}${reset}`, + `\r${orange}${bar} ${percent.toString().padStart(3)}%${reset} ${muted}${event.current}/${event.total}${reset} `, ) - if (event.current === event.total) process.stderr.write("\n") } else { process.stderr.write(`sqlite-migration:${percent}${EOL}`) } }, }) + if (tty) process.stderr.write("\n") if (tty) process.stderr.write("\x1b[?25h") else process.stderr.write(`sqlite-migration:done${EOL}`) UI.println(