circuschief 0.8.0 → 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.
Files changed (136) hide show
  1. package/package.json +1 -1
  2. package/packages/server/src/api/commandButtons.js +16 -15
  3. package/packages/server/src/api/projects-commandButtons.js +6 -6
  4. package/packages/server/src/api/projects-session-create.js +109 -0
  5. package/packages/server/src/api/projects-session-defaults.js +51 -0
  6. package/packages/server/src/api/projects-session-helpers.js +47 -1
  7. package/packages/server/src/api/projects-templates.js +38 -0
  8. package/packages/server/src/api/projects.js +28 -180
  9. package/packages/server/src/api/sessions-commands.js +21 -18
  10. package/packages/server/src/api/sessions-patch.js +41 -1
  11. package/packages/server/src/db/SessionRepository.js +1 -1
  12. package/packages/server/src/db/SessionTemplateRepository.js +23 -2
  13. package/packages/server/src/db/migrations/canvasItemsMigrations.js +109 -0
  14. package/packages/server/src/db/migrations/conversationsMigrations.js +187 -0
  15. package/packages/server/src/db/migrations/index.js +225 -6
  16. package/packages/server/src/db/migrations/kanbanMigrations.js +99 -0
  17. package/packages/server/src/db/migrations/miscMigrations.js +244 -0
  18. package/packages/server/src/db/migrations/projectsMigrations.js +130 -0
  19. package/packages/server/src/db/migrations/providerCommitAttributionMigrations.js +30 -0
  20. package/packages/server/src/db/migrations/providerMigrations.js +165 -0
  21. package/packages/server/src/db/migrations/sessionTableRecreate.js +136 -0
  22. package/packages/server/src/db/migrations/sessionsMigrations.js +300 -0
  23. package/packages/server/src/db/session-helpers.js +26 -1
  24. package/packages/server/src/schema.sql +4 -0
  25. package/packages/server/src/services/commandButtonPrompts.js +9 -7
  26. package/packages/server/src/services/gitCommitAttribution.js +38 -8
  27. package/packages/server/src/services/gitDiff.js +132 -0
  28. package/packages/server/src/services/gitRepoUrl.js +174 -0
  29. package/packages/server/src/services/gitService.js +37 -309
  30. package/packages/server/src/services/gitWorktree.js +127 -0
  31. package/packages/server/src/services/sessionPrompts.js +1 -1
  32. package/packages/shared/src/contracts/sessions.js +27 -1
  33. package/packages/shared/src/contracts/templates.js +10 -0
  34. package/packages/web/dist/assets/{ActiveSessionsView-B0XHqLmv.js → ActiveSessionsView-Cxh8mHmB.js} +1 -1
  35. package/packages/web/dist/assets/{AgentLogsView-DmsjUMlB.js → AgentLogsView-xdfI2bR6.js} +2 -2
  36. package/packages/web/dist/assets/ApiClient-DfbJwzpz.js +1 -0
  37. package/packages/web/dist/assets/ArchiveConfirmModal-DXZYdzHR.js +1 -0
  38. package/packages/web/dist/assets/CommandButtonDetailView-D8xfqLAp.js +1 -0
  39. package/packages/web/dist/assets/CommandButtonDetailView-D9zjx9ME.css +1 -0
  40. package/packages/web/dist/assets/EffortLevelSelector-D2Hdzc_8.js +1 -0
  41. package/packages/web/dist/assets/{GeneralSettingsView-D1nI8_zk.js → GeneralSettingsView-sPXkLlLy.js} +1 -1
  42. package/packages/web/dist/assets/{InputWithButton-CAkttyqx.js → InputWithButton-B-o0DgMH.js} +1 -1
  43. package/packages/web/dist/assets/{InterpolationHelp-BO1j9Z3_.js → InterpolationHelp-Dxn1li4l.js} +1 -1
  44. package/packages/web/dist/assets/MarkdownEditor-D4Kbb-9l.js +2 -0
  45. package/packages/web/dist/assets/ModelSelector-72C7MUH4.js +1 -0
  46. package/packages/web/dist/assets/{ModelSelector-BSxKUSus.css → ModelSelector-BNYKujL-.css} +1 -1
  47. package/packages/web/dist/assets/NewSessionView-BR_COfgW.js +3 -0
  48. package/packages/web/dist/assets/{NewSessionView-BDPb-1qr.css → NewSessionView-DBl7T2Xp.css} +1 -1
  49. package/packages/web/dist/assets/ProjectEditView-DbqTbA0q.css +1 -0
  50. package/packages/web/dist/assets/ProjectEditView-WImU7sNd.js +1 -0
  51. package/packages/web/dist/assets/{ProjectListView-DcNyuINs.js → ProjectListView-CYmmAcBD.js} +1 -1
  52. package/packages/web/dist/assets/{ProjectNewView-B5YV62hv.js → ProjectNewView-DEhqw3Jv.js} +1 -1
  53. package/packages/web/dist/assets/ProvidersView-XZh3jkmH.js +1 -0
  54. package/packages/web/dist/assets/QuickResponsesPanel-BqmnTd-D.js +1 -0
  55. package/packages/web/dist/assets/QuickResponsesPanel-dk-Rj8xx.css +1 -0
  56. package/packages/web/dist/assets/ResizableTextarea-BQNw5e0C.css +1 -0
  57. package/packages/web/dist/assets/ResizableTextarea-DpWdIAP6.js +1 -0
  58. package/packages/web/dist/assets/SessionCard-Bw77-KwD.js +1 -0
  59. package/packages/web/dist/assets/SessionDetailView-B59TEkr-.js +36 -0
  60. package/packages/web/dist/assets/SessionDetailView-CKVBnR4T.css +1 -0
  61. package/packages/web/dist/assets/{SessionFormOptions-B6AxyREh.js → SessionFormOptions-hqijxc0S.js} +1 -1
  62. package/packages/web/dist/assets/{SessionListView-B5_6gW49.css → SessionListView-3-xx6EVs.css} +1 -1
  63. package/packages/web/dist/assets/SessionListView-DYXHM9I-.js +1 -0
  64. package/packages/web/dist/assets/{SessionLogStream-LlZ3z_Xj.js → SessionLogStream-5NfVr9pF.js} +6 -6
  65. package/packages/web/dist/assets/{SettingsView-CTGiGvR2.js → SettingsView-DI8ncOAV.js} +1 -1
  66. package/packages/web/dist/assets/{SlashCommandWizard-Cy04d7-o.js → SlashCommandWizard-BQ_rMzn-.js} +1 -1
  67. package/packages/web/dist/assets/{SummarySettingsView-BR2ZjEa3.js → SummarySettingsView-C2Qs35mm.js} +1 -1
  68. package/packages/web/dist/assets/TemplateDetailView-B5NI2oTR.css +1 -0
  69. package/packages/web/dist/assets/TemplateDetailView-zVkIvgtu.js +1 -0
  70. package/packages/web/dist/assets/{commandButtons-BfqR-fqq.js → commandButtons-CoU3G4zK.js} +1 -1
  71. package/packages/web/dist/assets/index-9yF1uCCA.js +1 -0
  72. package/packages/web/dist/assets/index-BKstCaYU.js +1 -0
  73. package/packages/web/dist/assets/index-BhbH7eOk.js +1 -0
  74. package/packages/web/dist/assets/{index-DgkC10TW.js → index-BjuRttEY.js} +3 -3
  75. package/packages/web/dist/assets/index-Bo7PdwM5.js +1 -0
  76. package/packages/web/dist/assets/index-C2QFVD7d.js +83 -0
  77. package/packages/web/dist/assets/index-C7Ww2auW.js +1 -0
  78. package/packages/web/dist/assets/index-CAGdsDh7.js +1 -0
  79. package/packages/web/dist/assets/index-CLRsVASf.js +3 -0
  80. package/packages/web/dist/assets/{index-DtfUt785.js → index-CP-SxOlV.js} +1 -1
  81. package/packages/web/dist/assets/index-CslU0psO.js +1 -0
  82. package/packages/web/dist/assets/index-DI4NxaWD.js +1 -0
  83. package/packages/web/dist/assets/index-DOzONENy.js +1 -0
  84. package/packages/web/dist/assets/index-DUa7adFh.js +1 -0
  85. package/packages/web/dist/assets/index-DZBpETI5.js +1 -0
  86. package/packages/web/dist/assets/index-DsjWqc6R.js +7 -0
  87. package/packages/web/dist/assets/index-c99Bo3JV.js +1 -0
  88. package/packages/web/dist/assets/index-mT1JpxDc.js +1 -0
  89. package/packages/web/dist/assets/index-rkQx2tso.js +1 -0
  90. package/packages/web/dist/assets/{index-BY174HVJ.css → index-uySCcnA_.css} +1 -1
  91. package/packages/web/dist/assets/projectDefaults-B8esIcYq.js +1 -0
  92. package/packages/web/dist/assets/{projects-DXYQNJIi.js → projects-C-8PSxKi.js} +1 -1
  93. package/packages/web/dist/assets/{providers-1bnH-exJ.js → providers-oXifvvqN.js} +1 -1
  94. package/packages/web/dist/assets/{sessions-6zGUlFrt.js → sessions-Nq5VafSf.js} +1 -1
  95. package/packages/web/dist/assets/{settings-MbfRir0d.js → settings-DtpuiyT6.js} +1 -1
  96. package/packages/web/dist/index.html +2 -2
  97. package/packages/web/dist/assets/ApiClient-C3ztI9s9.js +0 -1
  98. package/packages/web/dist/assets/ArchiveConfirmModal-BlCyn5Vt.js +0 -1
  99. package/packages/web/dist/assets/CommandButtonDetailView-CdSCPp78.js +0 -1
  100. package/packages/web/dist/assets/CommandButtonDetailView-DBm3rzhw.css +0 -1
  101. package/packages/web/dist/assets/EffortLevelSelector-hc2MNKg6.js +0 -1
  102. package/packages/web/dist/assets/MarkdownEditor-ucRAP_UM.js +0 -2
  103. package/packages/web/dist/assets/ModelSelector-CwTz8ZWO.js +0 -1
  104. package/packages/web/dist/assets/NewSessionView-BsDrp8mj.js +0 -3
  105. package/packages/web/dist/assets/ProjectEditView-CwTOeSun.js +0 -1
  106. package/packages/web/dist/assets/ProjectEditView-J15mcsWz.css +0 -1
  107. package/packages/web/dist/assets/ProvidersView-nY9GnDdO.js +0 -1
  108. package/packages/web/dist/assets/QuickResponseSettings-B352c75l.css +0 -1
  109. package/packages/web/dist/assets/QuickResponseSettings-BQwQXuL7.js +0 -1
  110. package/packages/web/dist/assets/QuickResponsesPanel-BlFDvnZ2.css +0 -1
  111. package/packages/web/dist/assets/QuickResponsesPanel-BzSYcCSP.js +0 -1
  112. package/packages/web/dist/assets/ResizableTextarea-B3YIdIXv.js +0 -1
  113. package/packages/web/dist/assets/ResizableTextarea-DsU3TVwF.css +0 -1
  114. package/packages/web/dist/assets/SessionCard-CjE1tXiT.js +0 -1
  115. package/packages/web/dist/assets/SessionDetailView-3cPZrbS3.js +0 -36
  116. package/packages/web/dist/assets/SessionDetailView-CZRZMrfM.css +0 -1
  117. package/packages/web/dist/assets/SessionListView-CLXBfLcq.js +0 -1
  118. package/packages/web/dist/assets/TemplateDetailView-DH6Oswsp.js +0 -1
  119. package/packages/web/dist/assets/TemplateDetailView-DT2m06W7.css +0 -1
  120. package/packages/web/dist/assets/index-1zziPL6l.js +0 -1
  121. package/packages/web/dist/assets/index-7kzHPxSF.js +0 -1
  122. package/packages/web/dist/assets/index-B0N_obMc.js +0 -1
  123. package/packages/web/dist/assets/index-BNk_gdfI.js +0 -1
  124. package/packages/web/dist/assets/index-CSqaAH-0.js +0 -1
  125. package/packages/web/dist/assets/index-C_q4WlK8.js +0 -1
  126. package/packages/web/dist/assets/index-D1wpU4y0.js +0 -7
  127. package/packages/web/dist/assets/index-D5zCA8sD.js +0 -1
  128. package/packages/web/dist/assets/index-DGR8ELWY.js +0 -1
  129. package/packages/web/dist/assets/index-DHga8pXo.js +0 -1
  130. package/packages/web/dist/assets/index-DSby02Wl.js +0 -1
  131. package/packages/web/dist/assets/index-DqjXJTVI.js +0 -1
  132. package/packages/web/dist/assets/index-_4S2uLDI.js +0 -1
  133. package/packages/web/dist/assets/index-fK8FIZgP.js +0 -83
  134. package/packages/web/dist/assets/index-gmiZeFXN.js +0 -1
  135. package/packages/web/dist/assets/index-irD539ZM.js +0 -3
  136. package/packages/web/dist/assets/index-yq-E1Y00.js +0 -1
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Migrations for the conversations, conversation_messages, and message_attachments tables.
3
+ * Each export is an array of { name, up(db) } migration objects.
4
+ */
5
+ import crypto from 'crypto';
6
+ import { addColumnIfMissing, getColumns } from './migrationUtils.js';
7
+
8
+ // Table name constants for migrations
9
+ const TABLE_CONVERSATIONS = 'conversations';
10
+
11
+ // Column type constants
12
+ const COL_INTEGER_DEFAULT_0 = 'INTEGER DEFAULT 0';
13
+
14
+ /**
15
+ * Create default conversations for existing sessions that don't have any
16
+ * and associate orphaned messages with the default conversation.
17
+ */
18
+ function migrateExistingSessionsToConversations(db) {
19
+ const sessionsWithoutConversations = db
20
+ .prepare(
21
+ `
22
+ SELECT DISTINCT s.id FROM sessions s
23
+ LEFT JOIN conversations c ON c.session_id = s.id
24
+ WHERE c.id IS NULL
25
+ AND EXISTS (SELECT 1 FROM conversation_messages m WHERE m.session_id = s.id)
26
+ `
27
+ )
28
+ .all();
29
+
30
+ for (const session of sessionsWithoutConversations) {
31
+ const convId = crypto.randomUUID();
32
+ const now = Date.now();
33
+
34
+ db.prepare(
35
+ `INSERT INTO conversations (id, session_id, name, is_active, created_at, updated_at)
36
+ VALUES (?, ?, ?, 1, ?, ?)`
37
+ ).run(convId, session.id, 'Initial', now, now);
38
+
39
+ db.prepare(
40
+ `UPDATE conversation_messages SET conversation_id = ? WHERE session_id = ? AND conversation_id IS NULL`
41
+ ).run(convId, session.id);
42
+ }
43
+ }
44
+
45
+ /** @type {Array<{name: string, up: (db: import('better-sqlite3').Database) => void}>} */
46
+ export const conversationsMigrations = [
47
+ // --- Session summaries PR columns ---
48
+ {
49
+ name: 'session_summaries-add-pr_merged',
50
+ up(db) { addColumnIfMissing(db, 'session_summaries', 'pr_merged', 'INTEGER'); },
51
+ },
52
+ {
53
+ name: 'session_summaries-add-pr_state',
54
+ up(db) { addColumnIfMissing(db, 'session_summaries', 'pr_state', 'TEXT'); },
55
+ },
56
+ {
57
+ name: 'session_summaries-add-has_merge_conflicts',
58
+ up(db) { addColumnIfMissing(db, 'session_summaries', 'has_merge_conflicts', 'INTEGER'); },
59
+ },
60
+ {
61
+ name: 'session_summaries-add-ci_status',
62
+ up(db) { addColumnIfMissing(db, 'session_summaries', 'ci_status', 'TEXT'); },
63
+ },
64
+ {
65
+ name: 'session_summaries-add-ci_failures',
66
+ up(db) { addColumnIfMissing(db, 'session_summaries', 'ci_failures', 'TEXT'); },
67
+ },
68
+ {
69
+ name: 'session_summaries-add-last_summarized_message_id',
70
+ up(db) { addColumnIfMissing(db, 'session_summaries', 'last_summarized_message_id', 'TEXT'); },
71
+ },
72
+
73
+ // --- Message attachments ---
74
+ {
75
+ name: 'message_attachments-add-file_path',
76
+ up(db) { addColumnIfMissing(db, 'message_attachments', 'file_path', 'TEXT'); },
77
+ },
78
+
79
+ // --- Conversation messages: conversation_id ---
80
+ {
81
+ name: 'conversation_messages-add-conversation_id',
82
+ up(db) {
83
+ const columns = getColumns(db, 'conversation_messages');
84
+ if (!columns.includes('conversation_id')) {
85
+ db.exec(
86
+ 'ALTER TABLE conversation_messages ADD COLUMN conversation_id TEXT REFERENCES conversations(id) ON DELETE CASCADE'
87
+ );
88
+ db.exec(
89
+ 'CREATE INDEX IF NOT EXISTS idx_messages_conversation ON conversation_messages(conversation_id)'
90
+ );
91
+ }
92
+ },
93
+ },
94
+
95
+ // --- Migrate existing sessions to conversations ---
96
+ {
97
+ name: 'conversations-migrate-existing-sessions',
98
+ up(db) { migrateExistingSessionsToConversations(db); },
99
+ },
100
+
101
+ // --- Conversations: claude_session_id ---
102
+ {
103
+ name: 'conversations-add-claude_session_id',
104
+ up(db) { addColumnIfMissing(db, TABLE_CONVERSATIONS, 'claude_session_id', 'TEXT'); },
105
+ },
106
+
107
+ // --- Conversations token usage ---
108
+ {
109
+ name: 'conversations-add-input_tokens',
110
+ up(db) { addColumnIfMissing(db, TABLE_CONVERSATIONS, 'input_tokens', COL_INTEGER_DEFAULT_0); },
111
+ },
112
+ {
113
+ name: 'conversations-add-output_tokens',
114
+ up(db) { addColumnIfMissing(db, TABLE_CONVERSATIONS, 'output_tokens', COL_INTEGER_DEFAULT_0); },
115
+ },
116
+ {
117
+ name: 'conversations-add-thinking_tokens',
118
+ up(db) { addColumnIfMissing(db, TABLE_CONVERSATIONS, 'thinking_tokens', COL_INTEGER_DEFAULT_0); },
119
+ },
120
+ {
121
+ name: 'conversations-add-cache_read_input_tokens',
122
+ up(db) { addColumnIfMissing(db, TABLE_CONVERSATIONS, 'cache_read_input_tokens', COL_INTEGER_DEFAULT_0); },
123
+ },
124
+ {
125
+ name: 'conversations-add-cache_creation_input_tokens',
126
+ up(db) { addColumnIfMissing(db, TABLE_CONVERSATIONS, 'cache_creation_input_tokens', COL_INTEGER_DEFAULT_0); },
127
+ },
128
+ {
129
+ name: 'conversations-add-web_search_requests',
130
+ up(db) { addColumnIfMissing(db, TABLE_CONVERSATIONS, 'web_search_requests', COL_INTEGER_DEFAULT_0); },
131
+ },
132
+ {
133
+ name: 'conversations-add-context_window',
134
+ up(db) { addColumnIfMissing(db, TABLE_CONVERSATIONS, 'context_window', 'INTEGER DEFAULT 200000'); },
135
+ },
136
+ {
137
+ name: 'conversations-add-model',
138
+ up(db) { addColumnIfMissing(db, TABLE_CONVERSATIONS, 'model', 'TEXT'); },
139
+ },
140
+
141
+ // --- Conversation branching ---
142
+ {
143
+ name: 'conversations-add-parent_conversation_id',
144
+ up(db) {
145
+ const columns = getColumns(db, TABLE_CONVERSATIONS);
146
+ if (!columns.includes('parent_conversation_id')) {
147
+ db.exec(
148
+ 'ALTER TABLE conversations ADD COLUMN parent_conversation_id TEXT REFERENCES conversations(id) ON DELETE SET NULL'
149
+ );
150
+ db.exec(
151
+ 'CREATE INDEX IF NOT EXISTS idx_conversations_parent ON conversations(parent_conversation_id)'
152
+ );
153
+ }
154
+ },
155
+ },
156
+ {
157
+ name: 'conversations-add-branch_from_message_id',
158
+ up(db) {
159
+ addColumnIfMissing(
160
+ db, TABLE_CONVERSATIONS, 'branch_from_message_id',
161
+ 'TEXT REFERENCES conversation_messages(id) ON DELETE SET NULL'
162
+ );
163
+ },
164
+ },
165
+
166
+ // --- Session todos: conversation_id ---
167
+ {
168
+ name: 'session_todos-add-conversation_id',
169
+ up(db) {
170
+ const columns = getColumns(db, 'session_todos');
171
+ if (!columns.includes('conversation_id')) {
172
+ db.exec(
173
+ 'ALTER TABLE session_todos ADD COLUMN conversation_id TEXT REFERENCES conversations(id) ON DELETE CASCADE'
174
+ );
175
+ db.exec(
176
+ 'CREATE INDEX IF NOT EXISTS idx_todos_conversation ON session_todos(conversation_id)'
177
+ );
178
+ }
179
+ },
180
+ },
181
+
182
+ // --- Conversation messages: model ---
183
+ {
184
+ name: 'conversation_messages-add-model',
185
+ up(db) { addColumnIfMissing(db, 'conversation_messages', 'model', 'TEXT'); },
186
+ },
187
+ ];
@@ -1,3 +1,44 @@
1
+ /**
2
+ * Central registry of all database migrations in execution order.
3
+ *
4
+ * Each migration is idempotent (checks before acting), so the exact ordering
5
+ * only matters where there are hard dependencies (e.g. a table must exist
6
+ * before a column references it via FK).
7
+ *
8
+ * The ordering below mirrors the original #runMigrations() top-to-bottom flow
9
+ * as closely as possible.
10
+ */
11
+ import { sessionsMigrations } from './sessionsMigrations.js';
12
+ import { projectsMigrations } from './projectsMigrations.js';
13
+ import { conversationsMigrations } from './conversationsMigrations.js';
14
+ import { canvasItemsMigrations } from './canvasItemsMigrations.js';
15
+ import { miscMigrations } from './miscMigrations.js';
16
+ import { kanbanMigrations } from './kanbanMigrations.js';
17
+ import { providerMigrations } from './providerMigrations.js';
18
+ import { providerCommitAttributionMigrations } from './providerCommitAttributionMigrations.js';
19
+
20
+ /**
21
+ * Build a lookup map from a migrations array keyed by migration name.
22
+ * @param {Array<{name: string, up: Function}>} migrations
23
+ * @returns {Map<string, {name: string, up: Function}>}
24
+ */
25
+ function toLookup(migrations) {
26
+ const map = new Map();
27
+ for (const m of migrations) {
28
+ map.set(m.name, m);
29
+ }
30
+ return map;
31
+ }
32
+
33
+ const s = toLookup(sessionsMigrations);
34
+ const p = toLookup(projectsMigrations);
35
+ const c = toLookup(conversationsMigrations);
36
+ const ci = toLookup(canvasItemsMigrations);
37
+ const m = toLookup(miscMigrations);
38
+ const k = toLookup(kanbanMigrations);
39
+ const pr = toLookup(providerMigrations);
40
+ const pca = toLookup(providerCommitAttributionMigrations);
41
+
1
42
  /**
2
43
  * Repair sessions whose parent link was lost during schema consolidation.
3
44
  *
@@ -32,13 +73,191 @@ export const repairMissingSessionParentsFromWorktree = {
32
73
  };
33
74
 
34
75
  /**
35
- * Post-baseline database migrations.
36
- *
37
- * Pre-release migration history was consolidated into schema.sql. Future
38
- * shipped schema changes should be added here as explicit migrations.
76
+ * Flat, ordered list of every migration, matching the original execution order
77
+ * from DatabaseManager.#runMigrations().
39
78
  *
40
79
  * @type {Array<{name: string, up: (db: import('better-sqlite3').Database) => void}>}
41
80
  */
42
- export const allMigrations = [
81
+ // Validate that all migrations are properly defined
82
+ function validateMigrations(migrationsArray) {
83
+ for (const migration of migrationsArray) {
84
+ if (!migration || typeof migration.up !== 'function') {
85
+ const name = migration?.name || 'unknown';
86
+ throw new Error(`Migration "${name}" is undefined or missing an up() function`);
87
+ }
88
+ }
89
+ return migrationsArray;
90
+ }
91
+
92
+ export const allMigrations = validateMigrations([
93
+ // --- Sessions initial columns ---
94
+ s.get('sessions-add-cost_usd'),
95
+ s.get('sessions-add-claude_session_id'),
96
+ s.get('sessions-add-model'),
97
+ s.get('sessions-add-provider_id-early'),
98
+ s.get('sessions-add-effort_level'),
99
+
100
+ // --- Projects columns ---
101
+ p.get('projects-add-system_prompt'),
102
+ p.get('projects-add-on_session_created'),
103
+ p.get('projects-add-on_session_deleted'),
104
+ p.get('projects-add-repo_url'),
105
+ p.get('projects-add-worktree_path'),
106
+ p.get('projects-drop-summary-columns'),
107
+
108
+ // --- Sessions scheduling columns ---
109
+ s.get('sessions-add-scheduled_at'),
110
+ s.get('sessions-add-reschedule_delay_minutes'),
111
+ s.get('sessions-add-auto_reschedule_enabled'),
112
+ s.get('sessions-add-reschedule_on_token_limit'),
113
+ s.get('sessions-add-reschedule_on_service_error'),
114
+ s.get('sessions-add-max_reschedule_count'),
115
+ s.get('sessions-add-max_total_tokens'),
116
+ s.get('sessions-add-reschedule_count'),
117
+ s.get('sessions-add-reschedule_at_token_count'),
118
+
119
+ // --- Sessions status constraint migration (table recreation) ---
120
+ s.get('sessions-migrate-status-constraint'),
121
+
122
+ // --- Session summaries PR columns ---
123
+ c.get('session_summaries-add-pr_merged'),
124
+ c.get('session_summaries-add-pr_state'),
125
+ c.get('session_summaries-add-has_merge_conflicts'),
126
+ c.get('session_summaries-add-ci_status'),
127
+ c.get('session_summaries-add-ci_failures'),
128
+ c.get('session_summaries-add-last_summarized_message_id'),
129
+
130
+ // --- Sessions template chaining ---
131
+ s.get('sessions-add-next_template_id'),
132
+ s.get('sessions-add-parent_session_id'),
133
+ s.get('sessions-template-chaining-indexes'),
134
+
135
+ // --- Message attachments ---
136
+ c.get('message_attachments-add-file_path'),
137
+
138
+ // --- Conversation messages: conversation_id ---
139
+ c.get('conversation_messages-add-conversation_id'),
140
+
141
+ // --- Migrate existing sessions to conversations ---
142
+ c.get('conversations-migrate-existing-sessions'),
143
+
144
+ // --- Canvas items ---
145
+ ci.get('canvas_items-migrate-type-constraint'),
146
+ ci.get('canvas_items-add-deleted_at'),
147
+ ci.get('canvas_items-drop-label'),
148
+ ci.get('canvas_items-add-updated_at'),
149
+
150
+ // --- Conversations: claude_session_id + token usage ---
151
+ c.get('conversations-add-claude_session_id'),
152
+ c.get('conversations-add-input_tokens'),
153
+ c.get('conversations-add-output_tokens'),
154
+ c.get('conversations-add-thinking_tokens'),
155
+ c.get('conversations-add-cache_read_input_tokens'),
156
+ c.get('conversations-add-cache_creation_input_tokens'),
157
+ c.get('conversations-add-web_search_requests'),
158
+ c.get('conversations-add-context_window'),
159
+ c.get('conversations-add-model'),
160
+
161
+ // --- Sessions token usage ---
162
+ s.get('sessions-add-input_tokens'),
163
+ s.get('sessions-add-output_tokens'),
164
+ s.get('sessions-add-thinking_tokens'),
165
+ s.get('sessions-add-cache_read_input_tokens'),
166
+ s.get('sessions-add-cache_creation_input_tokens'),
167
+ s.get('sessions-add-web_search_requests'),
168
+ s.get('sessions-add-context_window'),
169
+
170
+ // --- Sessions archived / starred / manually_named ---
171
+ s.get('sessions-add-archived'),
172
+ s.get('sessions-add-starred'),
173
+ s.get('sessions-add-manually_named'),
174
+ s.get('sessions-add-pr_url_auto_link_disabled'),
175
+
176
+ // --- Project session defaults table ---
177
+ p.get('project_session_defaults-create-table'),
178
+
179
+ // --- Command buttons ---
180
+ m.get('command_buttons-add-show_on_list'),
181
+
182
+ // --- Session todos ---
183
+ c.get('session_todos-add-conversation_id'),
184
+
185
+ // --- Conversation branching ---
186
+ c.get('conversations-add-parent_conversation_id'),
187
+ c.get('conversations-add-branch_from_message_id'),
188
+
189
+ // --- Sessions pending prompt / slash commands / pending model / auto send ---
190
+ s.get('sessions-add-pending_prompt'),
191
+ s.get('sessions-add-slash_commands'),
192
+ s.get('sessions-add-pending_model'),
193
+ s.get('sessions-add-auto_send_pending_prompt'),
194
+
195
+ // --- Session templates ---
196
+ m.get('session_templates-add-model'),
197
+ m.get('session_templates-add-mode'),
198
+ m.get('session_templates-add-effort_level'),
199
+ m.get('session_templates-add-quick-response-fields'),
200
+
201
+ // --- Conversation messages model ---
202
+ c.get('conversation_messages-add-model'),
203
+
204
+ // --- App settings table ---
205
+ m.get('app_settings-create-table'),
206
+
207
+ // --- Legacy model_providers cleanup ---
208
+ pr.get('model_providers-cleanup-legacy'),
209
+
210
+ // --- Providers + provider_models tables + seed ---
211
+ pr.get('providers-create-tables'),
212
+ pr.get('providers-add-kind'),
213
+ pr.get('providers-add-commit_attribution_override'),
214
+ pca.get('providers-normalize-commit_attribution_override'),
215
+ pr.get('providers-seed-built-in'),
216
+ pr.get('providers-seed-built-in-openai'),
217
+
218
+ // --- Sessions provider_id (from providers FK) ---
219
+ s.get('sessions-add-provider_id-from-providers'),
220
+
221
+ // --- Project session defaults provider_id / effort_level ---
222
+ p.get('project_session_defaults-add-provider_id'),
223
+ p.get('project_session_defaults-add-effort_level'),
224
+
225
+ // --- Update built-in models ---
226
+ pr.get('providers-update-built-in-models'),
227
+
228
+ // --- Sessions agent_type ---
229
+ s.get('sessions-add-agent_type'),
230
+
231
+ // --- Agent call logs table ---
232
+ m.get('agent_call_logs-create-table'),
233
+
234
+ // --- Kanban feature ---
235
+ k.get('projects-add-kanban_enabled'),
236
+ k.get('kanban-create-tables'),
237
+ k.get('sessions-add-target_lane_id'),
238
+ k.get('sessions-add-lane_trigger_depth'),
239
+ k.get('session_templates-add-target_lane_id'),
240
+ k.get('kanban_lanes-add-on_enter_prompt'),
241
+ k.get('kanban_lanes-add-agent-settings'),
242
+
243
+ // --- Sessions default mode / thinking defaults (table recreation) ---
244
+ s.get('sessions-migrate-default-mode-thinking'),
245
+
246
+ // --- Seed default global quick responses ---
247
+ m.get('quick_responses-seed-defaults'),
248
+
249
+ // --- Convert legacy quick responses into template-backed quick responses ---
250
+ m.get('session_templates-convert-quick-responses'),
251
+
252
+ // --- Seed default global session templates ---
253
+ m.get('session_templates-seed-defaults'),
254
+
255
+ // --- Update built-in Opus model to 4.7 ---
256
+ pr.get('providers-update-built-in-opus-4-7'),
257
+
258
+ // --- Project session defaults: add 'current' git mode ---
259
+ p.get('project_session_defaults-git_mode-add-current'),
260
+
261
+ // --- Repair missing session parent links from worktree paths ---
43
262
  repairMissingSessionParentsFromWorktree,
44
- ];
263
+ ]);
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Migrations for Kanban board feature: kanban_boards, kanban_lanes,
3
+ * kanban_cards, kanban_card_sessions tables, and related columns on
4
+ * projects, sessions, and session_templates.
5
+ */
6
+ import { addColumnIfMissing } from './migrationUtils.js';
7
+
8
+ export const kanbanMigrations = [
9
+ {
10
+ name: 'projects-add-kanban_enabled',
11
+ up(db) {
12
+ addColumnIfMissing(db, 'projects', 'kanban_enabled', 'INTEGER NOT NULL DEFAULT 1');
13
+ },
14
+ },
15
+ {
16
+ name: 'kanban-create-tables',
17
+ up(db) {
18
+ db.exec(`
19
+ CREATE TABLE IF NOT EXISTS kanban_boards (
20
+ id TEXT PRIMARY KEY,
21
+ project_id TEXT NOT NULL UNIQUE REFERENCES projects(id) ON DELETE CASCADE,
22
+ created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
23
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
24
+ );
25
+
26
+ CREATE TABLE IF NOT EXISTS kanban_lanes (
27
+ id TEXT PRIMARY KEY,
28
+ board_id TEXT NOT NULL REFERENCES kanban_boards(id) ON DELETE CASCADE,
29
+ name TEXT NOT NULL,
30
+ sort_order INTEGER NOT NULL DEFAULT 0,
31
+ on_enter_template_id TEXT REFERENCES session_templates(id) ON DELETE SET NULL,
32
+ created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
33
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
34
+ );
35
+
36
+ CREATE TABLE IF NOT EXISTS kanban_cards (
37
+ id TEXT PRIMARY KEY,
38
+ lane_id TEXT NOT NULL REFERENCES kanban_lanes(id) ON DELETE CASCADE,
39
+ sort_order INTEGER NOT NULL DEFAULT 0,
40
+ created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
41
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
42
+ );
43
+
44
+ CREATE TABLE IF NOT EXISTS kanban_card_sessions (
45
+ id TEXT PRIMARY KEY,
46
+ card_id TEXT NOT NULL REFERENCES kanban_cards(id) ON DELETE CASCADE,
47
+ session_id TEXT NOT NULL UNIQUE REFERENCES sessions(id) ON DELETE CASCADE,
48
+ created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
49
+ );
50
+
51
+ CREATE INDEX IF NOT EXISTS idx_kanban_boards_project ON kanban_boards(project_id);
52
+ CREATE INDEX IF NOT EXISTS idx_kanban_lanes_board ON kanban_lanes(board_id, sort_order);
53
+ CREATE INDEX IF NOT EXISTS idx_kanban_cards_lane ON kanban_cards(lane_id, sort_order);
54
+ CREATE INDEX IF NOT EXISTS idx_kanban_card_sessions_session ON kanban_card_sessions(session_id);
55
+ CREATE INDEX IF NOT EXISTS idx_kanban_card_sessions_card ON kanban_card_sessions(card_id);
56
+ `);
57
+ },
58
+ },
59
+ {
60
+ name: 'sessions-add-target_lane_id',
61
+ up(db) {
62
+ addColumnIfMissing(db, 'sessions', 'target_lane_id', 'TEXT REFERENCES kanban_lanes(id) ON DELETE SET NULL');
63
+ },
64
+ },
65
+ {
66
+ name: 'sessions-add-lane_trigger_depth',
67
+ up(db) {
68
+ addColumnIfMissing(db, 'sessions', 'lane_trigger_depth', 'INTEGER NOT NULL DEFAULT 0');
69
+ },
70
+ },
71
+ {
72
+ name: 'session_templates-add-target_lane_id',
73
+ up(db) {
74
+ addColumnIfMissing(db, 'session_templates', 'target_lane_id', 'TEXT REFERENCES kanban_lanes(id) ON DELETE SET NULL');
75
+ },
76
+ },
77
+ {
78
+ name: 'kanban_lanes-add-on_enter_prompt',
79
+ up(db) {
80
+ addColumnIfMissing(db, 'kanban_lanes', 'on_enter_prompt', 'TEXT');
81
+ },
82
+ },
83
+ {
84
+ name: 'kanban_lanes-add-agent-settings',
85
+ up(db) {
86
+ addColumnIfMissing(db, 'kanban_lanes', 'on_enter_mode', 'TEXT');
87
+ addColumnIfMissing(db, 'kanban_lanes', 'on_enter_model', 'TEXT');
88
+ addColumnIfMissing(db, 'kanban_lanes', 'on_enter_effort_level', 'TEXT');
89
+ addColumnIfMissing(db, 'kanban_lanes', 'on_enter_thinking_enabled', 'INTEGER');
90
+ addColumnIfMissing(db, 'kanban_lanes', 'on_enter_auto_reschedule_enabled', 'INTEGER DEFAULT 0');
91
+ addColumnIfMissing(db, 'kanban_lanes', 'on_enter_reschedule_delay_minutes', 'INTEGER DEFAULT 60'); // keep in sync with DEFAULT_RESCHEDULE_DELAY_MINUTES
92
+ addColumnIfMissing(db, 'kanban_lanes', 'on_enter_reschedule_on_token_limit', 'INTEGER DEFAULT 1');
93
+ addColumnIfMissing(db, 'kanban_lanes', 'on_enter_reschedule_on_service_error', 'INTEGER DEFAULT 1');
94
+ addColumnIfMissing(db, 'kanban_lanes', 'on_enter_max_reschedule_count', 'INTEGER');
95
+ addColumnIfMissing(db, 'kanban_lanes', 'on_enter_max_total_tokens', 'INTEGER');
96
+ addColumnIfMissing(db, 'kanban_lanes', 'on_enter_reschedule_at_token_count', 'INTEGER');
97
+ },
98
+ },
99
+ ];