@vladimirven/openswe 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +203 -0
- package/CLAUDE.md +203 -0
- package/README.md +166 -0
- package/bun.lock +447 -0
- package/bunfig.toml +4 -0
- package/package.json +42 -0
- package/src/app.tsx +84 -0
- package/src/components/App.tsx +526 -0
- package/src/components/ConfirmDialog.tsx +88 -0
- package/src/components/Footer.tsx +50 -0
- package/src/components/HelpModal.tsx +136 -0
- package/src/components/IssueSelectorModal.tsx +701 -0
- package/src/components/ManualSessionModal.tsx +191 -0
- package/src/components/PhaseProgress.tsx +45 -0
- package/src/components/Preview.tsx +249 -0
- package/src/components/ProviderSwitcherModal.tsx +156 -0
- package/src/components/ScrollableText.tsx +120 -0
- package/src/components/SessionCard.tsx +60 -0
- package/src/components/SessionList.tsx +79 -0
- package/src/components/SessionTerminal.tsx +89 -0
- package/src/components/StatusBar.tsx +84 -0
- package/src/components/ThemeSwitcherModal.tsx +237 -0
- package/src/components/index.ts +58 -0
- package/src/components/session-utils.ts +337 -0
- package/src/components/theme.ts +206 -0
- package/src/components/types.ts +215 -0
- package/src/config/defaults.ts +44 -0
- package/src/config/env.ts +67 -0
- package/src/config/global.ts +252 -0
- package/src/config/index.ts +171 -0
- package/src/config/types.ts +131 -0
- package/src/core/.gitkeep +0 -0
- package/src/core/index.ts +5 -0
- package/src/core/parser.ts +62 -0
- package/src/core/process-manager.ts +52 -0
- package/src/core/session.ts +423 -0
- package/src/core/tmux.ts +206 -0
- package/src/git/.gitkeep +0 -0
- package/src/git/index.ts +8 -0
- package/src/git/repo.ts +443 -0
- package/src/git/worktree.ts +317 -0
- package/src/github/.gitkeep +0 -0
- package/src/github/client.ts +208 -0
- package/src/github/index.ts +8 -0
- package/src/github/issues.ts +351 -0
- package/src/index.ts +369 -0
- package/src/prompts/.gitkeep +0 -0
- package/src/prompts/index.ts +1 -0
- package/src/prompts/swe-system.ts +22 -0
- package/src/providers/claude.ts +103 -0
- package/src/providers/index.ts +21 -0
- package/src/providers/opencode.ts +98 -0
- package/src/providers/registry.ts +53 -0
- package/src/providers/types.ts +117 -0
- package/src/store/buffers.ts +234 -0
- package/src/store/db.test.ts +579 -0
- package/src/store/db.ts +249 -0
- package/src/store/index.ts +101 -0
- package/src/store/project.ts +119 -0
- package/src/store/schema.sql +71 -0
- package/src/store/sessions.ts +454 -0
- package/src/store/types.ts +194 -0
- package/src/theme/context.tsx +170 -0
- package/src/theme/custom.ts +134 -0
- package/src/theme/index.ts +58 -0
- package/src/theme/loader.ts +264 -0
- package/src/theme/themes/aura.json +69 -0
- package/src/theme/themes/ayu.json +80 -0
- package/src/theme/themes/carbonfox.json +248 -0
- package/src/theme/themes/catppuccin-frappe.json +233 -0
- package/src/theme/themes/catppuccin-macchiato.json +233 -0
- package/src/theme/themes/catppuccin.json +112 -0
- package/src/theme/themes/cobalt2.json +228 -0
- package/src/theme/themes/cursor.json +249 -0
- package/src/theme/themes/dracula.json +219 -0
- package/src/theme/themes/everforest.json +241 -0
- package/src/theme/themes/flexoki.json +237 -0
- package/src/theme/themes/github.json +233 -0
- package/src/theme/themes/gruvbox.json +242 -0
- package/src/theme/themes/kanagawa.json +77 -0
- package/src/theme/themes/lucent-orng.json +237 -0
- package/src/theme/themes/material.json +235 -0
- package/src/theme/themes/matrix.json +77 -0
- package/src/theme/themes/mercury.json +252 -0
- package/src/theme/themes/monokai.json +221 -0
- package/src/theme/themes/nightowl.json +221 -0
- package/src/theme/themes/nord.json +223 -0
- package/src/theme/themes/one-dark.json +84 -0
- package/src/theme/themes/opencode.json +245 -0
- package/src/theme/themes/orng.json +249 -0
- package/src/theme/themes/osaka-jade.json +93 -0
- package/src/theme/themes/palenight.json +222 -0
- package/src/theme/themes/rosepine.json +234 -0
- package/src/theme/themes/solarized.json +223 -0
- package/src/theme/themes/synthwave84.json +226 -0
- package/src/theme/themes/tokyonight.json +243 -0
- package/src/theme/themes/vercel.json +245 -0
- package/src/theme/themes/vesper.json +218 -0
- package/src/theme/themes/zenburn.json +223 -0
- package/src/theme/types.ts +225 -0
- package/src/types/sql.d.ts +4 -0
- package/src/utils/ansi-parser.ts +225 -0
- package/src/utils/format.ts +46 -0
- package/src/utils/id.ts +15 -0
- package/src/utils/logger.ts +112 -0
- package/src/utils/prerequisites.ts +118 -0
- package/src/utils/shell.ts +9 -0
- package/src/wizard/flows.ts +419 -0
- package/src/wizard/index.ts +37 -0
- package/src/wizard/prompts.ts +190 -0
- package/src/workspace/detect.test.ts +51 -0
- package/src/workspace/detect.ts +223 -0
- package/src/workspace/index.ts +71 -0
- package/src/workspace/init.ts +131 -0
- package/src/workspace/paths.ts +143 -0
- package/src/workspace/project.ts +164 -0
- package/tsconfig.json +22 -0
package/src/store/db.ts
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database connection and initialization
|
|
3
|
+
*
|
|
4
|
+
* Manages the SQLite database connection using Bun's native bun:sqlite.
|
|
5
|
+
* Uses WAL mode for better concurrency.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Database } from "bun:sqlite"
|
|
9
|
+
import { dirname } from "path"
|
|
10
|
+
import { getStateDatabasePath } from "../workspace/paths"
|
|
11
|
+
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Schema
|
|
14
|
+
// ============================================================================
|
|
15
|
+
|
|
16
|
+
// Import schema as raw text
|
|
17
|
+
import schema from "./schema.sql" with { type: "text" }
|
|
18
|
+
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// Manager Types
|
|
21
|
+
// ============================================================================
|
|
22
|
+
|
|
23
|
+
export interface DatabaseManager {
|
|
24
|
+
getDatabase: () => Database
|
|
25
|
+
initDatabase: (projectRoot: string) => Promise<void>
|
|
26
|
+
initDatabaseWithPath: (dbPath: string) => Promise<void>
|
|
27
|
+
closeDatabase: () => void
|
|
28
|
+
isDatabaseInitialized: () => boolean
|
|
29
|
+
getDatabasePath: () => string | null
|
|
30
|
+
getSchemaVersion: () => number
|
|
31
|
+
runMigrations: () => void
|
|
32
|
+
withTransaction: <T>(fn: () => T) => T
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// Database Manager Factory
|
|
37
|
+
// ============================================================================
|
|
38
|
+
|
|
39
|
+
export function createDatabaseManager(): DatabaseManager {
|
|
40
|
+
let db: Database | null = null
|
|
41
|
+
let currentDbPath: string | null = null
|
|
42
|
+
|
|
43
|
+
const getDatabase = (): Database => {
|
|
44
|
+
if (!db) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
"Database not initialized. Call initDatabase() first."
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
return db
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const applySchema = (): void => {
|
|
53
|
+
if (!db) {
|
|
54
|
+
throw new Error("Database not initialized")
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Execute schema as a transaction
|
|
58
|
+
db.exec(schema)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const initDatabase = async (projectRoot: string): Promise<void> => {
|
|
62
|
+
const dbPath = getStateDatabasePath(projectRoot)
|
|
63
|
+
|
|
64
|
+
// If already connected to this database, skip
|
|
65
|
+
if (db && currentDbPath === dbPath) {
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Close existing connection if any
|
|
70
|
+
if (db) {
|
|
71
|
+
closeDatabase()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Ensure directory exists
|
|
75
|
+
const dir = dirname(dbPath)
|
|
76
|
+
await Bun.write(dir + "/.keep", "")
|
|
77
|
+
|
|
78
|
+
// Open database connection
|
|
79
|
+
db = new Database(dbPath, { create: true })
|
|
80
|
+
currentDbPath = dbPath
|
|
81
|
+
|
|
82
|
+
// Enable WAL mode for better concurrency
|
|
83
|
+
db.exec("PRAGMA journal_mode = WAL")
|
|
84
|
+
|
|
85
|
+
// Enable foreign keys
|
|
86
|
+
db.exec("PRAGMA foreign_keys = ON")
|
|
87
|
+
|
|
88
|
+
// Apply schema
|
|
89
|
+
applySchema()
|
|
90
|
+
runMigrations()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const initDatabaseWithPath = async (dbPath: string): Promise<void> => {
|
|
94
|
+
// Close existing connection if any
|
|
95
|
+
if (db) {
|
|
96
|
+
closeDatabase()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// For in-memory databases, skip directory creation
|
|
100
|
+
if (dbPath !== ":memory:") {
|
|
101
|
+
const dir = dirname(dbPath)
|
|
102
|
+
await Bun.write(dir + "/.keep", "")
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Open database connection
|
|
106
|
+
db = new Database(dbPath, { create: true })
|
|
107
|
+
currentDbPath = dbPath
|
|
108
|
+
|
|
109
|
+
// Enable WAL mode for better concurrency (skip for in-memory)
|
|
110
|
+
if (dbPath !== ":memory:") {
|
|
111
|
+
db.exec("PRAGMA journal_mode = WAL")
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Enable foreign keys
|
|
115
|
+
db.exec("PRAGMA foreign_keys = ON")
|
|
116
|
+
|
|
117
|
+
// Apply schema
|
|
118
|
+
applySchema()
|
|
119
|
+
runMigrations()
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const closeDatabase = (): void => {
|
|
123
|
+
if (db) {
|
|
124
|
+
db.close()
|
|
125
|
+
db = null
|
|
126
|
+
currentDbPath = null
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const isDatabaseInitialized = (): boolean => {
|
|
131
|
+
return db !== null
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const getDatabasePath = (): string | null => {
|
|
135
|
+
return currentDbPath
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const getSchemaVersion = (): number => {
|
|
139
|
+
const database = getDatabase()
|
|
140
|
+
const result = database
|
|
141
|
+
.query<{ version: number }, []>(
|
|
142
|
+
"SELECT MAX(version) as version FROM schema_version"
|
|
143
|
+
)
|
|
144
|
+
.get()
|
|
145
|
+
return result?.version ?? 0
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const runMigrations = (): void => {
|
|
149
|
+
const version = getSchemaVersion()
|
|
150
|
+
const database = getDatabase()
|
|
151
|
+
|
|
152
|
+
// Migration to version 2: Add ai_session_data column
|
|
153
|
+
if (version < 2) {
|
|
154
|
+
// Check if column exists (for databases created with new schema)
|
|
155
|
+
const columns = database
|
|
156
|
+
.query<{ name: string }, []>("PRAGMA table_info(sessions)")
|
|
157
|
+
.all()
|
|
158
|
+
const hasColumn = columns.some((col) => col.name === "ai_session_data")
|
|
159
|
+
|
|
160
|
+
if (!hasColumn) {
|
|
161
|
+
database.exec("ALTER TABLE sessions ADD COLUMN ai_session_data TEXT")
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Update version
|
|
165
|
+
database.exec("INSERT OR REPLACE INTO schema_version (version) VALUES (2)")
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Add future migrations here:
|
|
169
|
+
// if (version < 3) { migrateTo3() }
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const withTransaction = <T>(fn: () => T): T => {
|
|
173
|
+
const database = getDatabase()
|
|
174
|
+
return database.transaction(fn)()
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
getDatabase,
|
|
179
|
+
initDatabase,
|
|
180
|
+
initDatabaseWithPath,
|
|
181
|
+
closeDatabase,
|
|
182
|
+
isDatabaseInitialized,
|
|
183
|
+
getDatabasePath,
|
|
184
|
+
getSchemaVersion,
|
|
185
|
+
runMigrations,
|
|
186
|
+
withTransaction,
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ============================================================================
|
|
191
|
+
// Active Database Manager
|
|
192
|
+
// ============================================================================
|
|
193
|
+
|
|
194
|
+
let activeManager = createDatabaseManager()
|
|
195
|
+
|
|
196
|
+
export function setDatabaseManager(manager: DatabaseManager): void {
|
|
197
|
+
activeManager = manager
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ============================================================================
|
|
201
|
+
// Default Manager Exports
|
|
202
|
+
// ============================================================================
|
|
203
|
+
|
|
204
|
+
export function getDatabase(): Database {
|
|
205
|
+
return activeManager.getDatabase()
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export async function initDatabase(projectRoot: string): Promise<void> {
|
|
209
|
+
return activeManager.initDatabase(projectRoot)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export async function initDatabaseWithPath(dbPath: string): Promise<void> {
|
|
213
|
+
return activeManager.initDatabaseWithPath(dbPath)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function closeDatabase(): void {
|
|
217
|
+
return activeManager.closeDatabase()
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function isDatabaseInitialized(): boolean {
|
|
221
|
+
return activeManager.isDatabaseInitialized()
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function getDatabasePath(): string | null {
|
|
225
|
+
return activeManager.getDatabasePath()
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function getSchemaVersion(): number {
|
|
229
|
+
return activeManager.getSchemaVersion()
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function runMigrations(): void {
|
|
233
|
+
return activeManager.runMigrations()
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function withTransaction<T>(fn: () => T): T {
|
|
237
|
+
return activeManager.withTransaction(fn)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ============================================================================
|
|
241
|
+
// Utility Functions
|
|
242
|
+
// ============================================================================
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Generate an ISO timestamp for the current time
|
|
246
|
+
*/
|
|
247
|
+
export function nowISO(): string {
|
|
248
|
+
return new Date().toISOString()
|
|
249
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Store module - SQLite database layer for OpenSWE
|
|
3
|
+
*
|
|
4
|
+
* Provides persistent storage for sessions, human tasks, project state,
|
|
5
|
+
* and output buffers using Bun's native bun:sqlite.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Types
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
export type {
|
|
13
|
+
Phase,
|
|
14
|
+
Status,
|
|
15
|
+
AISessionData,
|
|
16
|
+
Session,
|
|
17
|
+
CreateSessionInput,
|
|
18
|
+
UpdateSessionInput,
|
|
19
|
+
ProjectState,
|
|
20
|
+
CreateProjectInput,
|
|
21
|
+
OutputBuffer,
|
|
22
|
+
} from "./types"
|
|
23
|
+
|
|
24
|
+
export {
|
|
25
|
+
isValidPhase,
|
|
26
|
+
isValidStatus,
|
|
27
|
+
isValidAISessionData,
|
|
28
|
+
} from "./types"
|
|
29
|
+
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// Database Connection
|
|
32
|
+
// ============================================================================
|
|
33
|
+
|
|
34
|
+
export {
|
|
35
|
+
createDatabaseManager,
|
|
36
|
+
setDatabaseManager,
|
|
37
|
+
getDatabase,
|
|
38
|
+
initDatabase,
|
|
39
|
+
initDatabaseWithPath,
|
|
40
|
+
closeDatabase,
|
|
41
|
+
isDatabaseInitialized,
|
|
42
|
+
getDatabasePath,
|
|
43
|
+
getSchemaVersion,
|
|
44
|
+
runMigrations,
|
|
45
|
+
withTransaction,
|
|
46
|
+
nowISO,
|
|
47
|
+
} from "./db"
|
|
48
|
+
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// Project Operations
|
|
51
|
+
// ============================================================================
|
|
52
|
+
|
|
53
|
+
export {
|
|
54
|
+
getProject,
|
|
55
|
+
createProject,
|
|
56
|
+
updateLastOpened,
|
|
57
|
+
projectExists,
|
|
58
|
+
deleteProject,
|
|
59
|
+
} from "./project"
|
|
60
|
+
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// Session Operations
|
|
63
|
+
// ============================================================================
|
|
64
|
+
|
|
65
|
+
export {
|
|
66
|
+
getAllSessions,
|
|
67
|
+
getSession,
|
|
68
|
+
getSessionsByStatus,
|
|
69
|
+
getSessionsByPhase,
|
|
70
|
+
getActiveSessionCount,
|
|
71
|
+
getSessionCount,
|
|
72
|
+
createSession,
|
|
73
|
+
updateSession,
|
|
74
|
+
updateSessionPhase,
|
|
75
|
+
updateSessionStatus,
|
|
76
|
+
incrementRetryCount,
|
|
77
|
+
updateTokensUsed,
|
|
78
|
+
setPid,
|
|
79
|
+
setAISessionData,
|
|
80
|
+
getAISessionData,
|
|
81
|
+
deleteSession,
|
|
82
|
+
deleteAllSessions,
|
|
83
|
+
} from "./sessions"
|
|
84
|
+
|
|
85
|
+
// ============================================================================
|
|
86
|
+
// Buffer Operations
|
|
87
|
+
// ============================================================================
|
|
88
|
+
|
|
89
|
+
export {
|
|
90
|
+
MAX_BUFFER_LINES,
|
|
91
|
+
getBuffer,
|
|
92
|
+
getRecentLines,
|
|
93
|
+
getAllLines,
|
|
94
|
+
createBuffer,
|
|
95
|
+
ensureBuffer,
|
|
96
|
+
appendLines,
|
|
97
|
+
appendLine,
|
|
98
|
+
setLines,
|
|
99
|
+
clearBuffer,
|
|
100
|
+
deleteBuffer,
|
|
101
|
+
} from "./buffers"
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project state database operations
|
|
3
|
+
*
|
|
4
|
+
* Manages the project singleton in the database.
|
|
5
|
+
* This mirrors essential fields from ProjectConfig for querying.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { getDatabase, nowISO } from "./db"
|
|
9
|
+
import type { ProjectState, CreateProjectInput } from "./types"
|
|
10
|
+
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Database Row Type
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
15
|
+
interface ProjectRow {
|
|
16
|
+
id: number
|
|
17
|
+
repo_full_name: string
|
|
18
|
+
repo_url: string
|
|
19
|
+
created_at: string
|
|
20
|
+
last_opened_at: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ============================================================================
|
|
24
|
+
// Row Mapping
|
|
25
|
+
// ============================================================================
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Convert database row to ProjectState
|
|
29
|
+
*/
|
|
30
|
+
function rowToProject(row: ProjectRow): ProjectState {
|
|
31
|
+
return {
|
|
32
|
+
id: 1,
|
|
33
|
+
repoFullName: row.repo_full_name,
|
|
34
|
+
repoUrl: row.repo_url,
|
|
35
|
+
createdAt: row.created_at,
|
|
36
|
+
lastOpenedAt: row.last_opened_at,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ============================================================================
|
|
41
|
+
// CRUD Operations
|
|
42
|
+
// ============================================================================
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get the project state from the database
|
|
46
|
+
*
|
|
47
|
+
* @returns ProjectState or null if not created
|
|
48
|
+
*/
|
|
49
|
+
export function getProject(): ProjectState | null {
|
|
50
|
+
const db = getDatabase()
|
|
51
|
+
const row = db
|
|
52
|
+
.query<ProjectRow, []>("SELECT * FROM project WHERE id = 1")
|
|
53
|
+
.get()
|
|
54
|
+
|
|
55
|
+
return row ? rowToProject(row) : null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Create the project state (singleton)
|
|
60
|
+
*
|
|
61
|
+
* @param data - Project creation data
|
|
62
|
+
* @returns The created ProjectState
|
|
63
|
+
* @throws Error if project already exists
|
|
64
|
+
*/
|
|
65
|
+
export function createProject(data: CreateProjectInput): ProjectState {
|
|
66
|
+
const db = getDatabase()
|
|
67
|
+
const now = nowISO()
|
|
68
|
+
|
|
69
|
+
// Check if project already exists
|
|
70
|
+
const existing = getProject()
|
|
71
|
+
if (existing) {
|
|
72
|
+
throw new Error("Project already exists. Use updateProject instead.")
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
db.query(
|
|
76
|
+
`INSERT INTO project (id, repo_full_name, repo_url, created_at, last_opened_at)
|
|
77
|
+
VALUES (1, ?, ?, ?, ?)`
|
|
78
|
+
).run(
|
|
79
|
+
data.repoFullName,
|
|
80
|
+
data.repoUrl,
|
|
81
|
+
now,
|
|
82
|
+
now
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
id: 1,
|
|
87
|
+
repoFullName: data.repoFullName,
|
|
88
|
+
repoUrl: data.repoUrl,
|
|
89
|
+
createdAt: now,
|
|
90
|
+
lastOpenedAt: now,
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Update the lastOpenedAt timestamp
|
|
96
|
+
*/
|
|
97
|
+
export function updateLastOpened(): void {
|
|
98
|
+
const db = getDatabase()
|
|
99
|
+
const now = nowISO()
|
|
100
|
+
|
|
101
|
+
db.query("UPDATE project SET last_opened_at = ? WHERE id = 1").run(now)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Check if project exists in the database
|
|
106
|
+
*/
|
|
107
|
+
export function projectExists(): boolean {
|
|
108
|
+
return getProject() !== null
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Delete the project state
|
|
113
|
+
*
|
|
114
|
+
* Warning: This also cascades to delete all sessions and tasks.
|
|
115
|
+
*/
|
|
116
|
+
export function deleteProject(): void {
|
|
117
|
+
const db = getDatabase()
|
|
118
|
+
db.query("DELETE FROM project WHERE id = 1").run()
|
|
119
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
-- OpenSWE Database Schema
|
|
2
|
+
-- SQLite database for persistent storage of sessions, tasks, and project state
|
|
3
|
+
|
|
4
|
+
-- ============================================================================
|
|
5
|
+
-- Schema Version
|
|
6
|
+
-- ============================================================================
|
|
7
|
+
|
|
8
|
+
-- Track schema version for migrations
|
|
9
|
+
CREATE TABLE IF NOT EXISTS schema_version (
|
|
10
|
+
version INTEGER PRIMARY KEY,
|
|
11
|
+
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
-- Insert initial version if not exists
|
|
15
|
+
INSERT OR IGNORE INTO schema_version (version) VALUES (1);
|
|
16
|
+
|
|
17
|
+
-- ============================================================================
|
|
18
|
+
-- Project Table (Singleton)
|
|
19
|
+
-- ============================================================================
|
|
20
|
+
|
|
21
|
+
-- Project metadata - only one row (id = 1)
|
|
22
|
+
CREATE TABLE IF NOT EXISTS project (
|
|
23
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
24
|
+
repo_full_name TEXT NOT NULL,
|
|
25
|
+
repo_url TEXT NOT NULL,
|
|
26
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
27
|
+
last_opened_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
-- ============================================================================
|
|
31
|
+
-- Sessions Table
|
|
32
|
+
-- ============================================================================
|
|
33
|
+
|
|
34
|
+
-- Sessions represent work on specific issues or tasks
|
|
35
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
36
|
+
id TEXT PRIMARY KEY,
|
|
37
|
+
name TEXT NOT NULL,
|
|
38
|
+
issue_number INTEGER,
|
|
39
|
+
issue_title TEXT,
|
|
40
|
+
issue_body TEXT,
|
|
41
|
+
issue_url TEXT,
|
|
42
|
+
worktree_path TEXT NOT NULL,
|
|
43
|
+
branch_name TEXT NOT NULL,
|
|
44
|
+
phase TEXT NOT NULL DEFAULT 'pending',
|
|
45
|
+
status TEXT NOT NULL DEFAULT 'queued',
|
|
46
|
+
attention_reason TEXT,
|
|
47
|
+
retry_count INTEGER NOT NULL DEFAULT 0,
|
|
48
|
+
tokens_used INTEGER NOT NULL DEFAULT 0,
|
|
49
|
+
pid INTEGER,
|
|
50
|
+
ai_session_data TEXT, -- JSON: {backend, sessionId, ...}
|
|
51
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
52
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
-- Index for querying sessions by status
|
|
56
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(status);
|
|
57
|
+
|
|
58
|
+
-- Index for querying sessions by phase
|
|
59
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_phase ON sessions(phase);
|
|
60
|
+
|
|
61
|
+
-- ============================================================================
|
|
62
|
+
-- Output Buffers Table
|
|
63
|
+
-- ============================================================================
|
|
64
|
+
|
|
65
|
+
-- Circular buffers for session output (stored as JSON array)
|
|
66
|
+
CREATE TABLE IF NOT EXISTS output_buffers (
|
|
67
|
+
session_id TEXT PRIMARY KEY,
|
|
68
|
+
lines TEXT NOT NULL DEFAULT '[]',
|
|
69
|
+
last_updated TEXT NOT NULL DEFAULT (datetime('now')),
|
|
70
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
71
|
+
);
|