Skip to content

Commit a48a5a3

Browse files
committed
core: migrate from custom JSON storage to standard Drizzle migrations to improve database reliability and performance
This replaces the previous manual JSON file system with standard Drizzle migrations, enabling: - Proper database schema migrations with timestamp-based versioning - Batched migration for faster migration of large datasets - Better data integrity with proper table schemas instead of JSON blobs - Easier database upgrades and rollback capabilities Migration changes: - Todo table now uses individual columns with composite PK instead of JSON blob - Share table removes unused download share data - Session diff table moved from database table to file storage - All migrations now use proper Drizzle format with per-folder layout Users will see a one-time migration on next run that migrates existing JSON data to the new SQLite database.
1 parent 5e1639d commit a48a5a3

26 files changed

Lines changed: 1496 additions & 1404 deletions

File tree

.opencode/opencode.jsonc

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,6 @@
99
"options": {},
1010
},
1111
},
12-
"mcp": {
13-
"context7": {
14-
"type": "remote",
15-
"url": "https://mcp.context7.com/mcp",
16-
},
17-
},
1812
"tools": {
1913
"github-triage": false,
2014
"github-pr-search": false,

bun.lock

Lines changed: 38 additions & 139 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
"@tailwindcss/vite": "4.1.11",
3939
"diff": "8.0.2",
4040
"dompurify": "3.3.1",
41+
"drizzle-kit": "1.0.0-beta.12-a5629fb",
42+
"drizzle-orm": "1.0.0-beta.12-a5629fb",
4143
"ai": "5.0.119",
4244
"hono": "4.10.7",
4345
"hono-openapi": "1.1.2",

packages/console/core/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"@opencode-ai/console-resource": "workspace:*",
1313
"@planetscale/database": "1.19.0",
1414
"aws4fetch": "1.0.20",
15-
"drizzle-orm": "0.41.0",
15+
"drizzle-orm": "catalog:",
1616
"postgres": "3.4.7",
1717
"stripe": "18.0.0",
1818
"ulid": "catalog:",
@@ -43,7 +43,7 @@
4343
"@tsconfig/node22": "22.0.2",
4444
"@types/bun": "1.3.0",
4545
"@types/node": "catalog:",
46-
"drizzle-kit": "0.30.5",
46+
"drizzle-kit": "catalog:",
4747
"mysql2": "3.14.4",
4848
"typescript": "catalog:",
4949
"@typescript/native-preview": "catalog:"

packages/opencode/AGENTS.md

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,10 @@
1-
# opencode agent guidelines
1+
# opencode database guide
22

3-
## Build/Test Commands
3+
## Database
44

5-
- **Install**: `bun install`
6-
- **Run**: `bun run --conditions=browser ./src/index.ts`
7-
- **Typecheck**: `bun run typecheck` (npm run typecheck)
8-
- **Test**: `bun test` (runs all tests)
9-
- **Single test**: `bun test test/tool/tool.test.ts` (specific test file)
10-
11-
## Code Style
12-
13-
- **Runtime**: Bun with TypeScript ESM modules
14-
- **Imports**: Use relative imports for local modules, named imports preferred
15-
- **Types**: Zod schemas for validation, TypeScript interfaces for structure
16-
- **Naming**: camelCase for variables/functions, PascalCase for classes/namespaces
17-
- **Error handling**: Use Result patterns, avoid throwing exceptions in tools
18-
- **File structure**: Namespace-based organization (e.g., `Tool.define()`, `Session.create()`)
19-
20-
## Architecture
21-
22-
- **Tools**: Implement `Tool.Info` interface with `execute()` method
23-
- **Context**: Pass `sessionID` in tool context, use `App.provide()` for DI
24-
- **Validation**: All inputs validated with Zod schemas
25-
- **Logging**: Use `Log.create({ service: "name" })` pattern
26-
- **Storage**: Use `Storage` namespace for persistence
27-
- **API Client**: The TypeScript TUI (built with SolidJS + OpenTUI) communicates with the OpenCode server using `@opencode-ai/sdk`. When adding/modifying server endpoints in `packages/opencode/src/server/server.ts`, run `./script/generate.ts` to regenerate the SDK and related files.
5+
- **Schema**: Drizzle schema lives in `src/**/*.sql.ts`.
6+
- **Naming**: tables and columns use snake*case; join columns are `<entity>_id`; indexes are `<table>*<column>\_idx`.
7+
- **Migrations**: generated by Drizzle Kit using `drizzle.config.ts` (schema: `./src/**/*.sql.ts`, output: `./migration`).
8+
- **Command**: `bun run db generate --name <slug>`.
9+
- **Output**: creates `migration/<timestamp>_<slug>/migration.sql` and `snapshot.json`.
10+
- **Tests**: migration tests should read the per-folder layout (no `_journal.json`).

packages/opencode/drizzle.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@ export default defineConfig({
44
dialect: "sqlite",
55
schema: "./src/**/*.sql.ts",
66
out: "./migration",
7+
dbCredentials: {
8+
url: "/home/thdxr/.local/share/opencode/opencode.db",
9+
},
710
})

packages/opencode/migration/0000_magical_strong_guy.sql renamed to packages/opencode/migration/20260127173238_melted_union_jack/migration.sql

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
CREATE TABLE `project` (
2-
`id` text PRIMARY KEY NOT NULL,
2+
`id` text PRIMARY KEY,
33
`worktree` text NOT NULL,
44
`vcs` text,
55
`name` text,
@@ -12,38 +12,29 @@ CREATE TABLE `project` (
1212
);
1313
--> statement-breakpoint
1414
CREATE TABLE `message` (
15-
`id` text PRIMARY KEY NOT NULL,
15+
`id` text PRIMARY KEY,
1616
`session_id` text NOT NULL,
1717
`created_at` integer NOT NULL,
1818
`data` text NOT NULL,
19-
FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON UPDATE no action ON DELETE cascade
19+
CONSTRAINT `fk_message_session_id_session_id_fk` FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON DELETE CASCADE
2020
);
2121
--> statement-breakpoint
22-
CREATE INDEX `message_session_idx` ON `message` (`session_id`);--> statement-breakpoint
2322
CREATE TABLE `part` (
24-
`id` text PRIMARY KEY NOT NULL,
23+
`id` text PRIMARY KEY,
2524
`message_id` text NOT NULL,
2625
`session_id` text NOT NULL,
2726
`data` text NOT NULL,
28-
FOREIGN KEY (`message_id`) REFERENCES `message`(`id`) ON UPDATE no action ON DELETE cascade
27+
CONSTRAINT `fk_part_message_id_message_id_fk` FOREIGN KEY (`message_id`) REFERENCES `message`(`id`) ON DELETE CASCADE
2928
);
3029
--> statement-breakpoint
31-
CREATE INDEX `part_message_idx` ON `part` (`message_id`);--> statement-breakpoint
32-
CREATE INDEX `part_session_idx` ON `part` (`session_id`);--> statement-breakpoint
3330
CREATE TABLE `permission` (
34-
`project_id` text PRIMARY KEY NOT NULL,
31+
`project_id` text PRIMARY KEY,
3532
`data` text NOT NULL,
36-
FOREIGN KEY (`project_id`) REFERENCES `project`(`id`) ON UPDATE no action ON DELETE cascade
37-
);
38-
--> statement-breakpoint
39-
CREATE TABLE `session_diff` (
40-
`session_id` text PRIMARY KEY NOT NULL,
41-
`data` text NOT NULL,
42-
FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON UPDATE no action ON DELETE cascade
33+
CONSTRAINT `fk_permission_project_id_project_id_fk` FOREIGN KEY (`project_id`) REFERENCES `project`(`id`) ON DELETE CASCADE
4334
);
4435
--> statement-breakpoint
4536
CREATE TABLE `session` (
46-
`id` text PRIMARY KEY NOT NULL,
37+
`id` text PRIMARY KEY,
4738
`project_id` text NOT NULL,
4839
`parent_id` text,
4940
`slug` text NOT NULL,
@@ -64,24 +55,31 @@ CREATE TABLE `session` (
6455
`time_updated` integer NOT NULL,
6556
`time_compacting` integer,
6657
`time_archived` integer,
67-
FOREIGN KEY (`project_id`) REFERENCES `project`(`id`) ON UPDATE no action ON DELETE cascade
58+
CONSTRAINT `fk_session_project_id_project_id_fk` FOREIGN KEY (`project_id`) REFERENCES `project`(`id`) ON DELETE CASCADE
6859
);
6960
--> statement-breakpoint
70-
CREATE INDEX `session_project_idx` ON `session` (`project_id`);--> statement-breakpoint
71-
CREATE INDEX `session_parent_idx` ON `session` (`parent_id`);--> statement-breakpoint
7261
CREATE TABLE `todo` (
73-
`session_id` text PRIMARY KEY NOT NULL,
74-
`data` text NOT NULL,
75-
FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON UPDATE no action ON DELETE cascade
62+
`session_id` text NOT NULL,
63+
`id` text NOT NULL,
64+
`content` text NOT NULL,
65+
`status` text NOT NULL,
66+
`priority` text NOT NULL,
67+
`position` integer NOT NULL,
68+
CONSTRAINT `todo_pk` PRIMARY KEY(`session_id`, `id`),
69+
CONSTRAINT `fk_todo_session_id_session_id_fk` FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON DELETE CASCADE
7670
);
7771
--> statement-breakpoint
7872
CREATE TABLE `session_share` (
79-
`session_id` text PRIMARY KEY NOT NULL,
80-
`data` text NOT NULL,
81-
FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON UPDATE no action ON DELETE cascade
73+
`session_id` text PRIMARY KEY,
74+
`id` text NOT NULL,
75+
`secret` text NOT NULL,
76+
`url` text NOT NULL,
77+
CONSTRAINT `fk_session_share_session_id_session_id_fk` FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON DELETE CASCADE
8278
);
8379
--> statement-breakpoint
84-
CREATE TABLE `share` (
85-
`session_id` text PRIMARY KEY NOT NULL,
86-
`data` text NOT NULL
87-
);
80+
CREATE INDEX `message_session_idx` ON `message` (`session_id`);--> statement-breakpoint
81+
CREATE INDEX `part_message_idx` ON `part` (`message_id`);--> statement-breakpoint
82+
CREATE INDEX `part_session_idx` ON `part` (`session_id`);--> statement-breakpoint
83+
CREATE INDEX `session_project_idx` ON `session` (`project_id`);--> statement-breakpoint
84+
CREATE INDEX `session_parent_idx` ON `session` (`parent_id`);--> statement-breakpoint
85+
CREATE INDEX `todo_session_idx` ON `todo` (`session_id`);

0 commit comments

Comments
 (0)