@straiffi/archon 1.0.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/README.md +224 -0
- package/dist/cli.js +216 -0
- package/dist/client/assets/index-8_-boBBA.css +2 -0
- package/dist/client/assets/index-s_jjeqha.js +176 -0
- package/dist/client/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/dist/client/favicon.svg +62 -0
- package/dist/client/icons.svg +24 -0
- package/dist/client/index.html +14 -0
- package/dist/server/db.js +764 -0
- package/dist/server/db.js.map +1 -0
- package/dist/server/index.js +5134 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/lib/agent.js +1302 -0
- package/dist/server/lib/agent.js.map +1 -0
- package/dist/server/lib/buildChains.js +2 -0
- package/dist/server/lib/buildChains.js.map +1 -0
- package/dist/server/lib/buildFlow.js +59 -0
- package/dist/server/lib/buildFlow.js.map +1 -0
- package/dist/server/lib/buildSequences.js +599 -0
- package/dist/server/lib/buildSequences.js.map +1 -0
- package/dist/server/lib/bundleActivity.js +95 -0
- package/dist/server/lib/bundleActivity.js.map +1 -0
- package/dist/server/lib/bundlePullRequests.js +126 -0
- package/dist/server/lib/bundlePullRequests.js.map +1 -0
- package/dist/server/lib/chatMessages.js +60 -0
- package/dist/server/lib/chatMessages.js.map +1 -0
- package/dist/server/lib/chatTargets.js +123 -0
- package/dist/server/lib/chatTargets.js.map +1 -0
- package/dist/server/lib/chatTicketProposals.js +180 -0
- package/dist/server/lib/chatTicketProposals.js.map +1 -0
- package/dist/server/lib/chats.js +279 -0
- package/dist/server/lib/chats.js.map +1 -0
- package/dist/server/lib/config.js +3 -0
- package/dist/server/lib/config.js.map +1 -0
- package/dist/server/lib/cors.js +30 -0
- package/dist/server/lib/cors.js.map +1 -0
- package/dist/server/lib/directoryPicker.js +174 -0
- package/dist/server/lib/directoryPicker.js.map +1 -0
- package/dist/server/lib/git.js +1284 -0
- package/dist/server/lib/git.js.map +1 -0
- package/dist/server/lib/integrations/github.js +511 -0
- package/dist/server/lib/integrations/github.js.map +1 -0
- package/dist/server/lib/integrations/index.js +162 -0
- package/dist/server/lib/integrations/index.js.map +1 -0
- package/dist/server/lib/integrations/jira.js +283 -0
- package/dist/server/lib/integrations/jira.js.map +1 -0
- package/dist/server/lib/integrations/planning.js +27 -0
- package/dist/server/lib/integrations/planning.js.map +1 -0
- package/dist/server/lib/integrations/types.js +2 -0
- package/dist/server/lib/integrations/types.js.map +1 -0
- package/dist/server/lib/lightweightPrompt.js +88 -0
- package/dist/server/lib/lightweightPrompt.js.map +1 -0
- package/dist/server/lib/models.js +219 -0
- package/dist/server/lib/models.js.map +1 -0
- package/dist/server/lib/preview.js +377 -0
- package/dist/server/lib/preview.js.map +1 -0
- package/dist/server/lib/previewProxy.js +659 -0
- package/dist/server/lib/previewProxy.js.map +1 -0
- package/dist/server/lib/projectAutoConfig.js +682 -0
- package/dist/server/lib/projectAutoConfig.js.map +1 -0
- package/dist/server/lib/projectFileSuggestions.js +133 -0
- package/dist/server/lib/projectFileSuggestions.js.map +1 -0
- package/dist/server/lib/projectMemory.js +1519 -0
- package/dist/server/lib/projectMemory.js.map +1 -0
- package/dist/server/lib/projectMemoryPrompt.js +390 -0
- package/dist/server/lib/projectMemoryPrompt.js.map +1 -0
- package/dist/server/lib/projectMemoryScan.js +681 -0
- package/dist/server/lib/projectMemoryScan.js.map +1 -0
- package/dist/server/lib/projectMemorySuggestions.js +166 -0
- package/dist/server/lib/projectMemorySuggestions.js.map +1 -0
- package/dist/server/lib/projectMemoryTransfer.js +958 -0
- package/dist/server/lib/projectMemoryTransfer.js.map +1 -0
- package/dist/server/lib/projects.js +569 -0
- package/dist/server/lib/projects.js.map +1 -0
- package/dist/server/lib/promptSkills.js +28 -0
- package/dist/server/lib/promptSkills.js.map +1 -0
- package/dist/server/lib/queue.js +15 -0
- package/dist/server/lib/queue.js.map +1 -0
- package/dist/server/lib/reviewFindings.js +390 -0
- package/dist/server/lib/reviewFindings.js.map +1 -0
- package/dist/server/lib/run.js +416 -0
- package/dist/server/lib/run.js.map +1 -0
- package/dist/server/lib/runtimePaths.js +93 -0
- package/dist/server/lib/runtimePaths.js.map +1 -0
- package/dist/server/lib/shell.js +27 -0
- package/dist/server/lib/shell.js.map +1 -0
- package/dist/server/lib/skills.js +124 -0
- package/dist/server/lib/skills.js.map +1 -0
- package/dist/server/lib/startDev.js +18 -0
- package/dist/server/lib/startDev.js.map +1 -0
- package/dist/server/lib/staticClient.js +80 -0
- package/dist/server/lib/staticClient.js.map +1 -0
- package/dist/server/lib/terminal.js +366 -0
- package/dist/server/lib/terminal.js.map +1 -0
- package/dist/server/lib/ticketDependencies.js +174 -0
- package/dist/server/lib/ticketDependencies.js.map +1 -0
- package/dist/server/lib/ticketMessages.js +65 -0
- package/dist/server/lib/ticketMessages.js.map +1 -0
- package/dist/server/lib/ticketOpenQuestions.js +128 -0
- package/dist/server/lib/ticketOpenQuestions.js.map +1 -0
- package/dist/server/lib/ticketUndo.js +549 -0
- package/dist/server/lib/ticketUndo.js.map +1 -0
- package/dist/server/lib/tickets.js +981 -0
- package/dist/server/lib/tickets.js.map +1 -0
- package/dist/server/lib/types.js +2 -0
- package/dist/server/lib/types.js.map +1 -0
- package/dist/server/package.json +3 -0
- package/dist/server/workers/build.js +229 -0
- package/dist/server/workers/build.js.map +1 -0
- package/dist/server/workers/chat.js +190 -0
- package/dist/server/workers/chat.js.map +1 -0
- package/dist/server/workers/followUp.js +204 -0
- package/dist/server/workers/followUp.js.map +1 -0
- package/dist/server/workers/plan.js +1130 -0
- package/dist/server/workers/plan.js.map +1 -0
- package/dist/server/workers/planFollowUp.js +360 -0
- package/dist/server/workers/planFollowUp.js.map +1 -0
- package/dist/server/workers/review.js +167 -0
- package/dist/server/workers/review.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1,764 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import { ensureParentDirectory, resolveArchonDbPath } from './lib/runtimePaths.js';
|
|
3
|
+
const dbPath = ensureParentDirectory(resolveArchonDbPath());
|
|
4
|
+
const db = new Database(dbPath);
|
|
5
|
+
db.pragma('foreign_keys = ON');
|
|
6
|
+
db.exec(`
|
|
7
|
+
CREATE TABLE IF NOT EXISTS tickets (
|
|
8
|
+
id TEXT PRIMARY KEY,
|
|
9
|
+
title TEXT NOT NULL,
|
|
10
|
+
description TEXT,
|
|
11
|
+
state TEXT NOT NULL DEFAULT 'plan',
|
|
12
|
+
agent_status TEXT,
|
|
13
|
+
parked_at DATETIME,
|
|
14
|
+
auto_park_dismissed_at DATETIME,
|
|
15
|
+
branch TEXT,
|
|
16
|
+
agent_log TEXT,
|
|
17
|
+
model TEXT,
|
|
18
|
+
lane_order REAL,
|
|
19
|
+
done_order REAL,
|
|
20
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
21
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
22
|
+
)
|
|
23
|
+
`);
|
|
24
|
+
db.exec(`
|
|
25
|
+
CREATE TABLE IF NOT EXISTS worktree_bundles (
|
|
26
|
+
id TEXT PRIMARY KEY,
|
|
27
|
+
name TEXT NOT NULL,
|
|
28
|
+
branch TEXT NOT NULL,
|
|
29
|
+
kind TEXT NOT NULL DEFAULT 'worktree',
|
|
30
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
31
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
32
|
+
)
|
|
33
|
+
`);
|
|
34
|
+
db.exec(`
|
|
35
|
+
CREATE TABLE IF NOT EXISTS worktree_bases (
|
|
36
|
+
repo_path TEXT NOT NULL,
|
|
37
|
+
branch TEXT NOT NULL,
|
|
38
|
+
base_branch TEXT,
|
|
39
|
+
base_commit TEXT,
|
|
40
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
41
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
42
|
+
PRIMARY KEY (repo_path, branch)
|
|
43
|
+
)
|
|
44
|
+
`);
|
|
45
|
+
db.exec(`
|
|
46
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
47
|
+
id TEXT PRIMARY KEY,
|
|
48
|
+
name TEXT NOT NULL,
|
|
49
|
+
repo_path TEXT NOT NULL,
|
|
50
|
+
active_target_kind TEXT NOT NULL DEFAULT 'repo_root',
|
|
51
|
+
active_target_bundle_id TEXT,
|
|
52
|
+
worktree_cmd TEXT,
|
|
53
|
+
run_setup TEXT,
|
|
54
|
+
run_services TEXT,
|
|
55
|
+
run_ide TEXT,
|
|
56
|
+
preview_service_name TEXT,
|
|
57
|
+
preview_path TEXT,
|
|
58
|
+
preview_capability_mode TEXT,
|
|
59
|
+
helper_model TEXT,
|
|
60
|
+
helper_variant TEXT,
|
|
61
|
+
commit_message_rules TEXT,
|
|
62
|
+
auto_park_stale_tickets INTEGER NOT NULL DEFAULT 0,
|
|
63
|
+
memory_enabled INTEGER NOT NULL DEFAULT 0,
|
|
64
|
+
worktree_sync TEXT,
|
|
65
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
66
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
67
|
+
)
|
|
68
|
+
`);
|
|
69
|
+
db.exec(`
|
|
70
|
+
CREATE TABLE IF NOT EXISTS project_links (
|
|
71
|
+
project_id TEXT NOT NULL,
|
|
72
|
+
linked_project_id TEXT NOT NULL,
|
|
73
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
74
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
75
|
+
PRIMARY KEY (project_id, linked_project_id),
|
|
76
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
|
77
|
+
FOREIGN KEY (linked_project_id) REFERENCES projects(id) ON DELETE RESTRICT
|
|
78
|
+
)
|
|
79
|
+
`);
|
|
80
|
+
const ensureColumn = (table, column, type) => {
|
|
81
|
+
const columns = db.prepare(`PRAGMA table_info(${table})`).all();
|
|
82
|
+
if (!columns.some(existingColumn => existingColumn.name === column)) {
|
|
83
|
+
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${type}`);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
ensureColumn('tickets', 'model', 'TEXT');
|
|
87
|
+
ensureColumn('tickets', 'variant', 'TEXT');
|
|
88
|
+
ensureColumn('tickets', 'skills_json', 'TEXT');
|
|
89
|
+
ensureColumn('tickets', 'session_id', 'TEXT');
|
|
90
|
+
ensureColumn('tickets', 'planning_session_id', 'TEXT');
|
|
91
|
+
ensureColumn('tickets', 'project_id', 'TEXT');
|
|
92
|
+
ensureColumn('tickets', 'tool', 'TEXT');
|
|
93
|
+
ensureColumn('tickets', 'worktree_bundle_id', 'TEXT');
|
|
94
|
+
ensureColumn('tickets', 'planning_context_json', 'TEXT');
|
|
95
|
+
ensureColumn('tickets', 'build_completed_at', 'DATETIME');
|
|
96
|
+
ensureColumn('tickets', 'parked_at', 'DATETIME');
|
|
97
|
+
ensureColumn('tickets', 'auto_park_dismissed_at', 'DATETIME');
|
|
98
|
+
ensureColumn('tickets', 'streaming_response', 'TEXT');
|
|
99
|
+
ensureColumn('tickets', 'run_setup_status', 'TEXT');
|
|
100
|
+
ensureColumn('tickets', 'lane_order', 'REAL');
|
|
101
|
+
ensureColumn('tickets', 'done_order', 'REAL');
|
|
102
|
+
ensureColumn('tickets', 'external_provider', 'TEXT');
|
|
103
|
+
ensureColumn('tickets', 'external_id', 'TEXT');
|
|
104
|
+
ensureColumn('tickets', 'external_key', 'TEXT');
|
|
105
|
+
ensureColumn('tickets', 'external_url', 'TEXT');
|
|
106
|
+
ensureColumn('tickets', 'external_metadata_json', 'TEXT');
|
|
107
|
+
ensureColumn('worktree_bundles', 'project_id', 'TEXT');
|
|
108
|
+
ensureColumn('worktree_bundles', 'kind', "TEXT NOT NULL DEFAULT 'worktree'");
|
|
109
|
+
ensureColumn('projects', 'worktree_sync', 'TEXT');
|
|
110
|
+
ensureColumn('projects', 'active_target_kind', "TEXT NOT NULL DEFAULT 'repo_root'");
|
|
111
|
+
ensureColumn('projects', 'active_target_bundle_id', 'TEXT');
|
|
112
|
+
ensureColumn('projects', 'helper_model', 'TEXT');
|
|
113
|
+
ensureColumn('projects', 'helper_variant', 'TEXT');
|
|
114
|
+
ensureColumn('projects', 'commit_message_rules', 'TEXT');
|
|
115
|
+
ensureColumn('projects', 'preview_service_name', 'TEXT');
|
|
116
|
+
ensureColumn('projects', 'preview_path', 'TEXT');
|
|
117
|
+
ensureColumn('projects', 'preview_capability_mode', 'TEXT');
|
|
118
|
+
ensureColumn('projects', 'auto_park_stale_tickets', 'INTEGER NOT NULL DEFAULT 0');
|
|
119
|
+
ensureColumn('projects', 'memory_enabled', 'INTEGER NOT NULL DEFAULT 0');
|
|
120
|
+
ensureColumn('worktree_bases', 'repo_path', 'TEXT');
|
|
121
|
+
ensureColumn('worktree_bases', 'base_branch', 'TEXT');
|
|
122
|
+
ensureColumn('worktree_bases', 'base_commit', 'TEXT');
|
|
123
|
+
const ORDER_STEP = 1000;
|
|
124
|
+
const backfillTicketOrders = () => {
|
|
125
|
+
const activeRows = db.prepare(`
|
|
126
|
+
SELECT id, state
|
|
127
|
+
FROM tickets
|
|
128
|
+
WHERE lane_order IS NULL
|
|
129
|
+
ORDER BY state ASC, updated_at DESC, id DESC
|
|
130
|
+
`).all();
|
|
131
|
+
const nextLaneOrder = new Map([
|
|
132
|
+
['plan', 0],
|
|
133
|
+
['build', 0],
|
|
134
|
+
['review', 0],
|
|
135
|
+
]);
|
|
136
|
+
for (const row of activeRows) {
|
|
137
|
+
const nextOrder = (nextLaneOrder.get(row.state) ?? 0) + ORDER_STEP;
|
|
138
|
+
nextLaneOrder.set(row.state, nextOrder);
|
|
139
|
+
db.prepare('UPDATE tickets SET lane_order = ? WHERE id = ?').run(nextOrder, row.id);
|
|
140
|
+
}
|
|
141
|
+
const parkedRows = db.prepare(`
|
|
142
|
+
SELECT id
|
|
143
|
+
FROM tickets
|
|
144
|
+
WHERE parked_at IS NOT NULL AND done_order IS NULL
|
|
145
|
+
ORDER BY parked_at DESC, updated_at DESC, id DESC
|
|
146
|
+
`).all();
|
|
147
|
+
let nextDoneOrder = 0;
|
|
148
|
+
for (const row of parkedRows) {
|
|
149
|
+
nextDoneOrder += ORDER_STEP;
|
|
150
|
+
db.prepare('UPDATE tickets SET done_order = ? WHERE id = ?').run(nextDoneOrder, row.id);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
backfillTicketOrders();
|
|
154
|
+
const ensureUniqueBundleNames = () => {
|
|
155
|
+
const duplicateGroups = db.prepare(`
|
|
156
|
+
SELECT project_id, name
|
|
157
|
+
FROM worktree_bundles
|
|
158
|
+
GROUP BY project_id, name
|
|
159
|
+
HAVING COUNT(*) > 1
|
|
160
|
+
`).all();
|
|
161
|
+
const listDuplicates = db.prepare(`
|
|
162
|
+
SELECT id, name
|
|
163
|
+
FROM worktree_bundles
|
|
164
|
+
WHERE project_id IS ? AND name = ?
|
|
165
|
+
ORDER BY created_at ASC, id ASC
|
|
166
|
+
`);
|
|
167
|
+
const hasName = db.prepare(`
|
|
168
|
+
SELECT 1
|
|
169
|
+
FROM worktree_bundles
|
|
170
|
+
WHERE project_id IS ? AND name = ? AND id != ?
|
|
171
|
+
LIMIT 1
|
|
172
|
+
`);
|
|
173
|
+
const updateName = db.prepare(`
|
|
174
|
+
UPDATE worktree_bundles
|
|
175
|
+
SET name = ?, updated_at = CURRENT_TIMESTAMP
|
|
176
|
+
WHERE id = ?
|
|
177
|
+
`);
|
|
178
|
+
for (const duplicateGroup of duplicateGroups) {
|
|
179
|
+
const bundles = listDuplicates.all(duplicateGroup.project_id, duplicateGroup.name);
|
|
180
|
+
for (let index = 1; index < bundles.length; index += 1) {
|
|
181
|
+
const bundle = bundles[index];
|
|
182
|
+
let suffix = index + 1;
|
|
183
|
+
let candidateName = `${duplicateGroup.name} ${suffix}`;
|
|
184
|
+
while (hasName.get(duplicateGroup.project_id, candidateName, bundle.id)) {
|
|
185
|
+
suffix += 1;
|
|
186
|
+
candidateName = `${duplicateGroup.name} ${suffix}`;
|
|
187
|
+
}
|
|
188
|
+
updateName.run(candidateName, bundle.id);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
const ensureBundleIndexes = () => {
|
|
193
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_tickets_worktree_bundle_id ON tickets(worktree_bundle_id)');
|
|
194
|
+
db.exec('DROP INDEX IF EXISTS idx_worktree_bundles_branch');
|
|
195
|
+
db.exec('DROP INDEX IF EXISTS idx_worktree_bundles_project_name');
|
|
196
|
+
db.exec('DROP INDEX IF EXISTS idx_worktree_bundles_project_branch');
|
|
197
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_worktree_bundles_project_id ON worktree_bundles(project_id)');
|
|
198
|
+
ensureUniqueBundleNames();
|
|
199
|
+
db.exec("UPDATE worktree_bundles SET kind = 'worktree' WHERE kind IS NULL OR TRIM(kind) = ''");
|
|
200
|
+
db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_worktree_bundles_project_name ON worktree_bundles(project_id, name, kind)");
|
|
201
|
+
db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_worktree_bundles_project_branch ON worktree_bundles(project_id, branch, kind)");
|
|
202
|
+
db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_worktree_bundles_project_root ON worktree_bundles(project_id, kind) WHERE kind = 'project_root'");
|
|
203
|
+
};
|
|
204
|
+
ensureBundleIndexes();
|
|
205
|
+
db.exec(`
|
|
206
|
+
CREATE TABLE IF NOT EXISTS integration_connections (
|
|
207
|
+
id TEXT PRIMARY KEY,
|
|
208
|
+
provider TEXT NOT NULL,
|
|
209
|
+
project_id TEXT,
|
|
210
|
+
status TEXT NOT NULL,
|
|
211
|
+
config_json TEXT,
|
|
212
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
213
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
214
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
|
215
|
+
)
|
|
216
|
+
`);
|
|
217
|
+
db.exec(`
|
|
218
|
+
CREATE TABLE IF NOT EXISTS project_conventions (
|
|
219
|
+
id TEXT PRIMARY KEY,
|
|
220
|
+
project_id TEXT NOT NULL,
|
|
221
|
+
title TEXT NOT NULL,
|
|
222
|
+
scope TEXT,
|
|
223
|
+
instruction TEXT NOT NULL,
|
|
224
|
+
rationale TEXT,
|
|
225
|
+
stages_json TEXT,
|
|
226
|
+
priority TEXT NOT NULL DEFAULT 'normal',
|
|
227
|
+
injection_mode TEXT NOT NULL DEFAULT 'relevant',
|
|
228
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
229
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
230
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
231
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
|
232
|
+
)
|
|
233
|
+
`);
|
|
234
|
+
ensureColumn('project_conventions', 'injection_mode', "TEXT NOT NULL DEFAULT 'relevant'");
|
|
235
|
+
db.exec(`
|
|
236
|
+
CREATE TABLE IF NOT EXISTS project_decisions (
|
|
237
|
+
id TEXT PRIMARY KEY,
|
|
238
|
+
project_id TEXT NOT NULL,
|
|
239
|
+
title TEXT NOT NULL,
|
|
240
|
+
scope TEXT,
|
|
241
|
+
decision TEXT NOT NULL,
|
|
242
|
+
rationale TEXT,
|
|
243
|
+
implications_json TEXT,
|
|
244
|
+
status TEXT NOT NULL DEFAULT 'accepted',
|
|
245
|
+
source_ticket_id TEXT,
|
|
246
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
247
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
248
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
|
249
|
+
)
|
|
250
|
+
`);
|
|
251
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_project_conventions_project_status ON project_conventions(project_id, status)');
|
|
252
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_project_decisions_project_status ON project_decisions(project_id, status)');
|
|
253
|
+
db.exec(`
|
|
254
|
+
CREATE TABLE IF NOT EXISTS project_memory_suggestions (
|
|
255
|
+
id TEXT PRIMARY KEY,
|
|
256
|
+
project_id TEXT NOT NULL,
|
|
257
|
+
ticket_id TEXT,
|
|
258
|
+
chat_session_id TEXT,
|
|
259
|
+
source_stage TEXT NOT NULL,
|
|
260
|
+
kind TEXT NOT NULL,
|
|
261
|
+
title TEXT NOT NULL,
|
|
262
|
+
scope TEXT,
|
|
263
|
+
content_text TEXT NOT NULL,
|
|
264
|
+
rationale TEXT,
|
|
265
|
+
details_json TEXT,
|
|
266
|
+
duplicate_kind TEXT NOT NULL DEFAULT 'none',
|
|
267
|
+
duplicate_target_id TEXT,
|
|
268
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
269
|
+
resolved_memory_id TEXT,
|
|
270
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
271
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
272
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
|
273
|
+
FOREIGN KEY (ticket_id) REFERENCES tickets(id) ON DELETE CASCADE,
|
|
274
|
+
FOREIGN KEY (chat_session_id) REFERENCES chat_sessions(id) ON DELETE CASCADE,
|
|
275
|
+
CHECK (ticket_id IS NOT NULL OR chat_session_id IS NOT NULL)
|
|
276
|
+
)
|
|
277
|
+
`);
|
|
278
|
+
db.exec(`
|
|
279
|
+
CREATE TABLE IF NOT EXISTS project_context_scans (
|
|
280
|
+
id TEXT PRIMARY KEY,
|
|
281
|
+
project_id TEXT NOT NULL,
|
|
282
|
+
status TEXT NOT NULL,
|
|
283
|
+
repo_head TEXT,
|
|
284
|
+
repo_branch TEXT,
|
|
285
|
+
scanner_tool TEXT,
|
|
286
|
+
scanner_model TEXT,
|
|
287
|
+
scanner_variant TEXT,
|
|
288
|
+
summary_markdown TEXT,
|
|
289
|
+
error TEXT,
|
|
290
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
291
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
292
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
|
293
|
+
)
|
|
294
|
+
`);
|
|
295
|
+
db.exec(`
|
|
296
|
+
CREATE TABLE IF NOT EXISTS project_context_artifacts (
|
|
297
|
+
id TEXT PRIMARY KEY,
|
|
298
|
+
scan_id TEXT NOT NULL,
|
|
299
|
+
project_id TEXT NOT NULL,
|
|
300
|
+
kind TEXT NOT NULL,
|
|
301
|
+
title TEXT NOT NULL,
|
|
302
|
+
content_json TEXT,
|
|
303
|
+
content_markdown TEXT,
|
|
304
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
305
|
+
FOREIGN KEY (scan_id) REFERENCES project_context_scans(id) ON DELETE CASCADE,
|
|
306
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
|
307
|
+
)
|
|
308
|
+
`);
|
|
309
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_project_context_scans_project_status ON project_context_scans(project_id, status)');
|
|
310
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_project_context_scans_project_created_at ON project_context_scans(project_id, created_at DESC)');
|
|
311
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_project_context_artifacts_scan_id ON project_context_artifacts(scan_id)');
|
|
312
|
+
db.prepare(`
|
|
313
|
+
UPDATE project_context_scans
|
|
314
|
+
SET status = 'error',
|
|
315
|
+
error = CASE
|
|
316
|
+
WHEN error IS NULL OR TRIM(error) = '' THEN 'Server restarted before the scan completed.'
|
|
317
|
+
ELSE error
|
|
318
|
+
END,
|
|
319
|
+
updated_at = CURRENT_TIMESTAMP
|
|
320
|
+
WHERE status IN ('pending', 'running')
|
|
321
|
+
`).run();
|
|
322
|
+
db.exec(`
|
|
323
|
+
CREATE TABLE IF NOT EXISTS build_sequences (
|
|
324
|
+
id TEXT PRIMARY KEY,
|
|
325
|
+
project_id TEXT NOT NULL,
|
|
326
|
+
worktree_bundle_id TEXT,
|
|
327
|
+
kind TEXT NOT NULL,
|
|
328
|
+
root_ticket_id TEXT,
|
|
329
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
330
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
331
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
332
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
|
333
|
+
FOREIGN KEY (worktree_bundle_id) REFERENCES worktree_bundles(id) ON DELETE CASCADE,
|
|
334
|
+
FOREIGN KEY (root_ticket_id) REFERENCES tickets(id) ON DELETE SET NULL
|
|
335
|
+
)
|
|
336
|
+
`);
|
|
337
|
+
db.exec(`
|
|
338
|
+
CREATE TABLE IF NOT EXISTS build_sequence_tickets (
|
|
339
|
+
build_sequence_id TEXT NOT NULL,
|
|
340
|
+
ticket_id TEXT NOT NULL,
|
|
341
|
+
position INTEGER NOT NULL,
|
|
342
|
+
status TEXT NOT NULL,
|
|
343
|
+
entered_queue_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
344
|
+
started_at DATETIME,
|
|
345
|
+
finished_at DATETIME,
|
|
346
|
+
PRIMARY KEY (build_sequence_id, ticket_id),
|
|
347
|
+
FOREIGN KEY (build_sequence_id) REFERENCES build_sequences(id) ON DELETE CASCADE,
|
|
348
|
+
FOREIGN KEY (ticket_id) REFERENCES tickets(id) ON DELETE CASCADE
|
|
349
|
+
)
|
|
350
|
+
`);
|
|
351
|
+
db.exec(`
|
|
352
|
+
CREATE TABLE IF NOT EXISTS ticket_dependencies (
|
|
353
|
+
blocker_ticket_id TEXT NOT NULL,
|
|
354
|
+
dependent_ticket_id TEXT NOT NULL,
|
|
355
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
356
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
357
|
+
PRIMARY KEY (blocker_ticket_id, dependent_ticket_id),
|
|
358
|
+
FOREIGN KEY (blocker_ticket_id) REFERENCES tickets(id) ON DELETE CASCADE,
|
|
359
|
+
FOREIGN KEY (dependent_ticket_id) REFERENCES tickets(id) ON DELETE CASCADE
|
|
360
|
+
)
|
|
361
|
+
`);
|
|
362
|
+
db.exec(`
|
|
363
|
+
CREATE TABLE IF NOT EXISTS ticket_messages (
|
|
364
|
+
id TEXT PRIMARY KEY,
|
|
365
|
+
ticket_id TEXT NOT NULL,
|
|
366
|
+
role TEXT NOT NULL,
|
|
367
|
+
kind TEXT NOT NULL,
|
|
368
|
+
content TEXT NOT NULL,
|
|
369
|
+
bundle_visible_at DATETIME,
|
|
370
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
371
|
+
FOREIGN KEY (ticket_id) REFERENCES tickets(id) ON DELETE CASCADE
|
|
372
|
+
)
|
|
373
|
+
`);
|
|
374
|
+
ensureColumn('ticket_messages', 'bundle_visible_at', 'DATETIME');
|
|
375
|
+
db.prepare(`
|
|
376
|
+
UPDATE ticket_messages
|
|
377
|
+
SET bundle_visible_at = (
|
|
378
|
+
SELECT MIN(related.created_at)
|
|
379
|
+
FROM ticket_messages AS related
|
|
380
|
+
WHERE related.ticket_id = ticket_messages.ticket_id
|
|
381
|
+
AND related.kind != 'description'
|
|
382
|
+
)
|
|
383
|
+
WHERE ticket_messages.kind = 'description'
|
|
384
|
+
AND ticket_messages.bundle_visible_at IS NULL
|
|
385
|
+
AND EXISTS (
|
|
386
|
+
SELECT 1
|
|
387
|
+
FROM ticket_messages AS related
|
|
388
|
+
WHERE related.ticket_id = ticket_messages.ticket_id
|
|
389
|
+
AND related.kind != 'description'
|
|
390
|
+
)
|
|
391
|
+
`).run();
|
|
392
|
+
db.prepare(`
|
|
393
|
+
UPDATE ticket_messages
|
|
394
|
+
SET bundle_visible_at = (
|
|
395
|
+
SELECT tickets.updated_at
|
|
396
|
+
FROM tickets
|
|
397
|
+
WHERE tickets.id = ticket_messages.ticket_id
|
|
398
|
+
)
|
|
399
|
+
WHERE ticket_messages.kind = 'description'
|
|
400
|
+
AND ticket_messages.bundle_visible_at IS NULL
|
|
401
|
+
AND EXISTS (
|
|
402
|
+
SELECT 1
|
|
403
|
+
FROM tickets
|
|
404
|
+
WHERE tickets.id = ticket_messages.ticket_id
|
|
405
|
+
AND tickets.state != 'plan'
|
|
406
|
+
AND (tickets.agent_status IS NOT NULL OR tickets.session_id IS NOT NULL)
|
|
407
|
+
)
|
|
408
|
+
`).run();
|
|
409
|
+
db.exec(`
|
|
410
|
+
CREATE TABLE IF NOT EXISTS chat_sessions (
|
|
411
|
+
id TEXT PRIMARY KEY,
|
|
412
|
+
project_id TEXT NOT NULL,
|
|
413
|
+
title TEXT NOT NULL,
|
|
414
|
+
mode TEXT NOT NULL,
|
|
415
|
+
tool TEXT NOT NULL,
|
|
416
|
+
model TEXT,
|
|
417
|
+
variant TEXT,
|
|
418
|
+
skills_json TEXT,
|
|
419
|
+
agent_status TEXT,
|
|
420
|
+
agent_log TEXT,
|
|
421
|
+
streaming_response TEXT,
|
|
422
|
+
target_kind TEXT NOT NULL DEFAULT 'repo_root',
|
|
423
|
+
target_bundle_id TEXT,
|
|
424
|
+
target_branch_snapshot TEXT,
|
|
425
|
+
target_label_snapshot TEXT,
|
|
426
|
+
claude_session_id TEXT,
|
|
427
|
+
opencode_session_id TEXT,
|
|
428
|
+
usage_snapshot_context_window INTEGER,
|
|
429
|
+
usage_snapshot_max_output_tokens INTEGER,
|
|
430
|
+
usage_snapshot_total_tokens INTEGER,
|
|
431
|
+
usage_snapshot_input_tokens INTEGER,
|
|
432
|
+
usage_snapshot_output_tokens INTEGER,
|
|
433
|
+
usage_snapshot_cache_read_tokens INTEGER,
|
|
434
|
+
usage_snapshot_reasoning_tokens INTEGER,
|
|
435
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
436
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
437
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
|
438
|
+
)
|
|
439
|
+
`);
|
|
440
|
+
db.exec(`
|
|
441
|
+
CREATE TABLE IF NOT EXISTS chat_messages (
|
|
442
|
+
id TEXT PRIMARY KEY,
|
|
443
|
+
chat_session_id TEXT NOT NULL,
|
|
444
|
+
role TEXT NOT NULL,
|
|
445
|
+
kind TEXT NOT NULL,
|
|
446
|
+
content TEXT NOT NULL,
|
|
447
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
448
|
+
FOREIGN KEY (chat_session_id) REFERENCES chat_sessions(id) ON DELETE CASCADE
|
|
449
|
+
)
|
|
450
|
+
`);
|
|
451
|
+
ensureColumn('chat_sessions', 'usage_snapshot_context_window', 'INTEGER');
|
|
452
|
+
ensureColumn('chat_sessions', 'usage_snapshot_max_output_tokens', 'INTEGER');
|
|
453
|
+
ensureColumn('chat_sessions', 'usage_snapshot_total_tokens', 'INTEGER');
|
|
454
|
+
ensureColumn('chat_sessions', 'usage_snapshot_input_tokens', 'INTEGER');
|
|
455
|
+
ensureColumn('chat_sessions', 'usage_snapshot_output_tokens', 'INTEGER');
|
|
456
|
+
ensureColumn('chat_sessions', 'usage_snapshot_cache_read_tokens', 'INTEGER');
|
|
457
|
+
ensureColumn('chat_sessions', 'usage_snapshot_reasoning_tokens', 'INTEGER');
|
|
458
|
+
ensureColumn('chat_sessions', 'target_kind', "TEXT NOT NULL DEFAULT 'repo_root'");
|
|
459
|
+
ensureColumn('chat_sessions', 'target_bundle_id', 'TEXT');
|
|
460
|
+
ensureColumn('chat_sessions', 'target_branch_snapshot', 'TEXT');
|
|
461
|
+
ensureColumn('chat_sessions', 'target_label_snapshot', 'TEXT');
|
|
462
|
+
const createChatSessionsTableSql = `
|
|
463
|
+
CREATE TABLE chat_sessions (
|
|
464
|
+
id TEXT PRIMARY KEY,
|
|
465
|
+
project_id TEXT NOT NULL,
|
|
466
|
+
title TEXT NOT NULL,
|
|
467
|
+
mode TEXT NOT NULL,
|
|
468
|
+
tool TEXT NOT NULL,
|
|
469
|
+
model TEXT,
|
|
470
|
+
variant TEXT,
|
|
471
|
+
skills_json TEXT,
|
|
472
|
+
agent_status TEXT,
|
|
473
|
+
agent_log TEXT,
|
|
474
|
+
streaming_response TEXT,
|
|
475
|
+
target_kind TEXT NOT NULL DEFAULT 'repo_root',
|
|
476
|
+
target_bundle_id TEXT,
|
|
477
|
+
target_branch_snapshot TEXT,
|
|
478
|
+
target_label_snapshot TEXT,
|
|
479
|
+
claude_session_id TEXT,
|
|
480
|
+
opencode_session_id TEXT,
|
|
481
|
+
usage_snapshot_context_window INTEGER,
|
|
482
|
+
usage_snapshot_max_output_tokens INTEGER,
|
|
483
|
+
usage_snapshot_total_tokens INTEGER,
|
|
484
|
+
usage_snapshot_input_tokens INTEGER,
|
|
485
|
+
usage_snapshot_output_tokens INTEGER,
|
|
486
|
+
usage_snapshot_cache_read_tokens INTEGER,
|
|
487
|
+
usage_snapshot_reasoning_tokens INTEGER,
|
|
488
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
489
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
490
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
|
491
|
+
)
|
|
492
|
+
`;
|
|
493
|
+
const migrateChatSessionsUsageSnapshotTable = () => {
|
|
494
|
+
const columns = db.prepare('PRAGMA table_info(chat_sessions)').all();
|
|
495
|
+
if (columns.length === 0) {
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
const hasLegacyUsageColumns = columns.some(column => column.name.startsWith('latest_turn_'));
|
|
499
|
+
if (!hasLegacyUsageColumns) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
const resolveColumn = (preferred, legacy) => {
|
|
503
|
+
if (legacy && columns.some(column => column.name === legacy)) {
|
|
504
|
+
return legacy;
|
|
505
|
+
}
|
|
506
|
+
if (columns.some(column => column.name === preferred)) {
|
|
507
|
+
return preferred;
|
|
508
|
+
}
|
|
509
|
+
return 'NULL';
|
|
510
|
+
};
|
|
511
|
+
db.exec('PRAGMA foreign_keys = OFF');
|
|
512
|
+
try {
|
|
513
|
+
db.exec('BEGIN');
|
|
514
|
+
db.exec(createChatSessionsTableSql.replace('chat_sessions', 'chat_sessions_v2'));
|
|
515
|
+
db.exec(`
|
|
516
|
+
INSERT INTO chat_sessions_v2 (
|
|
517
|
+
id,
|
|
518
|
+
project_id,
|
|
519
|
+
title,
|
|
520
|
+
mode,
|
|
521
|
+
tool,
|
|
522
|
+
model,
|
|
523
|
+
variant,
|
|
524
|
+
skills_json,
|
|
525
|
+
agent_status,
|
|
526
|
+
agent_log,
|
|
527
|
+
streaming_response,
|
|
528
|
+
claude_session_id,
|
|
529
|
+
opencode_session_id,
|
|
530
|
+
usage_snapshot_context_window,
|
|
531
|
+
usage_snapshot_max_output_tokens,
|
|
532
|
+
usage_snapshot_total_tokens,
|
|
533
|
+
usage_snapshot_input_tokens,
|
|
534
|
+
usage_snapshot_output_tokens,
|
|
535
|
+
usage_snapshot_cache_read_tokens,
|
|
536
|
+
usage_snapshot_reasoning_tokens,
|
|
537
|
+
created_at,
|
|
538
|
+
updated_at
|
|
539
|
+
)
|
|
540
|
+
SELECT
|
|
541
|
+
id,
|
|
542
|
+
project_id,
|
|
543
|
+
title,
|
|
544
|
+
mode,
|
|
545
|
+
tool,
|
|
546
|
+
model,
|
|
547
|
+
variant,
|
|
548
|
+
skills_json,
|
|
549
|
+
agent_status,
|
|
550
|
+
agent_log,
|
|
551
|
+
streaming_response,
|
|
552
|
+
claude_session_id,
|
|
553
|
+
opencode_session_id,
|
|
554
|
+
${resolveColumn('usage_snapshot_context_window', 'latest_turn_context_window')},
|
|
555
|
+
${resolveColumn('usage_snapshot_max_output_tokens', 'latest_turn_max_output_tokens')},
|
|
556
|
+
${resolveColumn('usage_snapshot_total_tokens', 'latest_turn_total_tokens')},
|
|
557
|
+
${resolveColumn('usage_snapshot_input_tokens', 'latest_turn_input_tokens')},
|
|
558
|
+
${resolveColumn('usage_snapshot_output_tokens', 'latest_turn_output_tokens')},
|
|
559
|
+
${resolveColumn('usage_snapshot_cache_read_tokens', 'latest_turn_cache_read_tokens')},
|
|
560
|
+
${resolveColumn('usage_snapshot_reasoning_tokens', 'latest_turn_reasoning_tokens')},
|
|
561
|
+
created_at,
|
|
562
|
+
updated_at
|
|
563
|
+
FROM chat_sessions
|
|
564
|
+
`);
|
|
565
|
+
db.exec('DROP TABLE chat_sessions');
|
|
566
|
+
db.exec('ALTER TABLE chat_sessions_v2 RENAME TO chat_sessions');
|
|
567
|
+
db.exec('COMMIT');
|
|
568
|
+
}
|
|
569
|
+
catch (error) {
|
|
570
|
+
db.exec('ROLLBACK');
|
|
571
|
+
throw error;
|
|
572
|
+
}
|
|
573
|
+
finally {
|
|
574
|
+
db.exec('PRAGMA foreign_keys = ON');
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
migrateChatSessionsUsageSnapshotTable();
|
|
578
|
+
const migrateProjectMemorySuggestionsTable = () => {
|
|
579
|
+
const columns = db.prepare('PRAGMA table_info(project_memory_suggestions)').all();
|
|
580
|
+
if (columns.length === 0) {
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
const ticketIdColumn = columns.find(column => column.name === 'ticket_id');
|
|
584
|
+
const hasChatSessionId = columns.some(column => column.name === 'chat_session_id');
|
|
585
|
+
const needsMigration = !hasChatSessionId || ticketIdColumn?.notnull === 1;
|
|
586
|
+
if (!needsMigration) {
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
db.exec(`
|
|
590
|
+
CREATE TABLE project_memory_suggestions_v2 (
|
|
591
|
+
id TEXT PRIMARY KEY,
|
|
592
|
+
project_id TEXT NOT NULL,
|
|
593
|
+
ticket_id TEXT,
|
|
594
|
+
chat_session_id TEXT,
|
|
595
|
+
source_stage TEXT NOT NULL,
|
|
596
|
+
kind TEXT NOT NULL,
|
|
597
|
+
title TEXT NOT NULL,
|
|
598
|
+
scope TEXT,
|
|
599
|
+
content_text TEXT NOT NULL,
|
|
600
|
+
rationale TEXT,
|
|
601
|
+
details_json TEXT,
|
|
602
|
+
duplicate_kind TEXT NOT NULL DEFAULT 'none',
|
|
603
|
+
duplicate_target_id TEXT,
|
|
604
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
605
|
+
resolved_memory_id TEXT,
|
|
606
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
607
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
608
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
|
609
|
+
FOREIGN KEY (ticket_id) REFERENCES tickets(id) ON DELETE CASCADE,
|
|
610
|
+
FOREIGN KEY (chat_session_id) REFERENCES chat_sessions(id) ON DELETE CASCADE,
|
|
611
|
+
CHECK (ticket_id IS NOT NULL OR chat_session_id IS NOT NULL)
|
|
612
|
+
)
|
|
613
|
+
`);
|
|
614
|
+
db.exec(`
|
|
615
|
+
INSERT INTO project_memory_suggestions_v2 (
|
|
616
|
+
id,
|
|
617
|
+
project_id,
|
|
618
|
+
ticket_id,
|
|
619
|
+
chat_session_id,
|
|
620
|
+
source_stage,
|
|
621
|
+
kind,
|
|
622
|
+
title,
|
|
623
|
+
scope,
|
|
624
|
+
content_text,
|
|
625
|
+
rationale,
|
|
626
|
+
details_json,
|
|
627
|
+
duplicate_kind,
|
|
628
|
+
duplicate_target_id,
|
|
629
|
+
status,
|
|
630
|
+
resolved_memory_id,
|
|
631
|
+
created_at,
|
|
632
|
+
updated_at
|
|
633
|
+
)
|
|
634
|
+
SELECT
|
|
635
|
+
id,
|
|
636
|
+
project_id,
|
|
637
|
+
ticket_id,
|
|
638
|
+
NULL,
|
|
639
|
+
source_stage,
|
|
640
|
+
kind,
|
|
641
|
+
title,
|
|
642
|
+
scope,
|
|
643
|
+
content_text,
|
|
644
|
+
rationale,
|
|
645
|
+
details_json,
|
|
646
|
+
duplicate_kind,
|
|
647
|
+
duplicate_target_id,
|
|
648
|
+
status,
|
|
649
|
+
resolved_memory_id,
|
|
650
|
+
created_at,
|
|
651
|
+
updated_at
|
|
652
|
+
FROM project_memory_suggestions
|
|
653
|
+
`);
|
|
654
|
+
db.exec('DROP TABLE project_memory_suggestions');
|
|
655
|
+
db.exec('ALTER TABLE project_memory_suggestions_v2 RENAME TO project_memory_suggestions');
|
|
656
|
+
};
|
|
657
|
+
migrateProjectMemorySuggestionsTable();
|
|
658
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_project_memory_suggestions_project_status ON project_memory_suggestions(project_id, status)');
|
|
659
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_project_memory_suggestions_ticket_status ON project_memory_suggestions(ticket_id, status)');
|
|
660
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_project_memory_suggestions_chat_session_status ON project_memory_suggestions(chat_session_id, status)');
|
|
661
|
+
db.exec(`
|
|
662
|
+
CREATE TABLE IF NOT EXISTS ticket_stage_changes (
|
|
663
|
+
id TEXT PRIMARY KEY,
|
|
664
|
+
ticket_id TEXT NOT NULL,
|
|
665
|
+
project_id TEXT,
|
|
666
|
+
worktree_bundle_id TEXT,
|
|
667
|
+
branch TEXT NOT NULL,
|
|
668
|
+
stage TEXT NOT NULL,
|
|
669
|
+
baseline_snapshot_path TEXT,
|
|
670
|
+
baseline_build_completed_at DATETIME,
|
|
671
|
+
baseline_session_id TEXT,
|
|
672
|
+
diff_text TEXT,
|
|
673
|
+
changed_files_json TEXT,
|
|
674
|
+
diff_stats_json TEXT,
|
|
675
|
+
success_revision INTEGER DEFAULT 0,
|
|
676
|
+
last_success_at DATETIME,
|
|
677
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
678
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
679
|
+
FOREIGN KEY (ticket_id) REFERENCES tickets(id) ON DELETE CASCADE
|
|
680
|
+
)
|
|
681
|
+
`);
|
|
682
|
+
db.exec(`
|
|
683
|
+
CREATE TABLE IF NOT EXISTS review_runs (
|
|
684
|
+
id TEXT PRIMARY KEY,
|
|
685
|
+
ticket_id TEXT NOT NULL,
|
|
686
|
+
project_id TEXT,
|
|
687
|
+
worktree_bundle_id TEXT,
|
|
688
|
+
stage TEXT NOT NULL,
|
|
689
|
+
diff_signature TEXT NOT NULL,
|
|
690
|
+
workspace_signature TEXT,
|
|
691
|
+
message_id TEXT,
|
|
692
|
+
status TEXT NOT NULL,
|
|
693
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
694
|
+
FOREIGN KEY (ticket_id) REFERENCES tickets(id) ON DELETE CASCADE,
|
|
695
|
+
FOREIGN KEY (message_id) REFERENCES ticket_messages(id) ON DELETE SET NULL
|
|
696
|
+
)
|
|
697
|
+
`);
|
|
698
|
+
ensureColumn('review_runs', 'workspace_signature', 'TEXT');
|
|
699
|
+
db.exec(`
|
|
700
|
+
CREATE TABLE IF NOT EXISTS review_findings (
|
|
701
|
+
id TEXT PRIMARY KEY,
|
|
702
|
+
review_run_id TEXT NOT NULL,
|
|
703
|
+
ordinal INTEGER NOT NULL,
|
|
704
|
+
title TEXT NOT NULL,
|
|
705
|
+
severity TEXT NOT NULL,
|
|
706
|
+
file_path TEXT,
|
|
707
|
+
old_line_number INTEGER,
|
|
708
|
+
new_line_number INTEGER,
|
|
709
|
+
line_content TEXT,
|
|
710
|
+
hunk_header TEXT,
|
|
711
|
+
what_text TEXT NOT NULL,
|
|
712
|
+
why_text TEXT,
|
|
713
|
+
fix_text TEXT,
|
|
714
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
715
|
+
FOREIGN KEY (review_run_id) REFERENCES review_runs(id) ON DELETE CASCADE
|
|
716
|
+
)
|
|
717
|
+
`);
|
|
718
|
+
db.exec(`
|
|
719
|
+
CREATE TABLE IF NOT EXISTS bundle_pull_requests (
|
|
720
|
+
id TEXT PRIMARY KEY,
|
|
721
|
+
bundle_id TEXT NOT NULL,
|
|
722
|
+
project_id TEXT NOT NULL,
|
|
723
|
+
provider TEXT NOT NULL,
|
|
724
|
+
repo_host TEXT NOT NULL,
|
|
725
|
+
repo_owner TEXT NOT NULL,
|
|
726
|
+
repo_name TEXT NOT NULL,
|
|
727
|
+
pr_number INTEGER NOT NULL,
|
|
728
|
+
pr_url TEXT NOT NULL,
|
|
729
|
+
pr_title TEXT NOT NULL,
|
|
730
|
+
base_branch TEXT NOT NULL,
|
|
731
|
+
head_branch TEXT NOT NULL,
|
|
732
|
+
state TEXT NOT NULL,
|
|
733
|
+
is_draft INTEGER NOT NULL DEFAULT 0,
|
|
734
|
+
merged_at DATETIME,
|
|
735
|
+
closed_at DATETIME,
|
|
736
|
+
github_pr_node_id TEXT,
|
|
737
|
+
last_synced_at DATETIME,
|
|
738
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
739
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
740
|
+
FOREIGN KEY (bundle_id) REFERENCES worktree_bundles(id) ON DELETE CASCADE,
|
|
741
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
|
742
|
+
)
|
|
743
|
+
`);
|
|
744
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_ticket_dependencies_blocker_ticket_id ON ticket_dependencies(blocker_ticket_id)');
|
|
745
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_ticket_dependencies_dependent_ticket_id ON ticket_dependencies(dependent_ticket_id)');
|
|
746
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_ticket_messages_ticket_id_created_at ON ticket_messages(ticket_id, created_at)');
|
|
747
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_chat_sessions_project_updated_at ON chat_sessions(project_id, updated_at DESC)');
|
|
748
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_chat_messages_session_created_at ON chat_messages(chat_session_id, created_at)');
|
|
749
|
+
db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_ticket_stage_changes_ticket_stage ON ticket_stage_changes(ticket_id, stage)');
|
|
750
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_ticket_stage_changes_bundle_stage_success ON ticket_stage_changes(project_id, worktree_bundle_id, stage, success_revision)');
|
|
751
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_review_runs_ticket_created_at ON review_runs(ticket_id, created_at)');
|
|
752
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_review_runs_bundle_created_at ON review_runs(project_id, worktree_bundle_id, created_at)');
|
|
753
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_review_findings_review_run_ordinal ON review_findings(review_run_id, ordinal)');
|
|
754
|
+
db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_bundle_pull_requests_project_bundle_provider ON bundle_pull_requests(project_id, bundle_id, provider)');
|
|
755
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_bundle_pull_requests_project_state ON bundle_pull_requests(project_id, state)');
|
|
756
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_project_links_project_id ON project_links(project_id)');
|
|
757
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_project_links_linked_project_id ON project_links(linked_project_id)');
|
|
758
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_build_sequences_project_status ON build_sequences(project_id, status)');
|
|
759
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_build_sequences_bundle_status ON build_sequences(worktree_bundle_id, status)');
|
|
760
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_build_sequence_tickets_sequence_position ON build_sequence_tickets(build_sequence_id, position)');
|
|
761
|
+
db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_integration_connections_provider_project ON integration_connections(provider, project_id)');
|
|
762
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_tickets_external_key ON tickets(external_provider, external_key)');
|
|
763
|
+
export default db;
|
|
764
|
+
//# sourceMappingURL=db.js.map
|