sqlew 3.6.10 → 3.7.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 (370) hide show
  1. package/CHANGELOG.md +318 -0
  2. package/README.md +54 -39
  3. package/assets/config.example.toml +93 -0
  4. package/assets/kanban-visualizer.png +0 -0
  5. package/assets/sample-agents/sqlew-architect.md +32 -13
  6. package/assets/sample-agents/sqlew-researcher.md +70 -17
  7. package/assets/sample-agents/sqlew-scrum-master.md +60 -25
  8. package/assets/schema.sql +2 -2
  9. package/dist/adapters/auth/auth-factory.d.ts +86 -0
  10. package/dist/adapters/auth/auth-factory.d.ts.map +1 -0
  11. package/dist/adapters/auth/auth-factory.js +103 -0
  12. package/dist/adapters/auth/auth-factory.js.map +1 -0
  13. package/dist/adapters/auth/auth-types.d.ts +30 -0
  14. package/dist/adapters/auth/auth-types.d.ts.map +1 -0
  15. package/dist/adapters/auth/auth-types.js +30 -0
  16. package/dist/adapters/auth/auth-types.js.map +1 -0
  17. package/dist/adapters/auth/base-auth-provider.d.ts +327 -0
  18. package/dist/adapters/auth/base-auth-provider.d.ts.map +1 -0
  19. package/dist/adapters/auth/base-auth-provider.js +111 -0
  20. package/dist/adapters/auth/base-auth-provider.js.map +1 -0
  21. package/dist/adapters/auth/direct-auth-provider.d.ts +356 -0
  22. package/dist/adapters/auth/direct-auth-provider.d.ts.map +1 -0
  23. package/dist/adapters/auth/direct-auth-provider.js +406 -0
  24. package/dist/adapters/auth/direct-auth-provider.js.map +1 -0
  25. package/dist/adapters/base-adapter.d.ts +638 -0
  26. package/dist/adapters/base-adapter.d.ts.map +1 -0
  27. package/dist/adapters/base-adapter.js +557 -0
  28. package/dist/adapters/base-adapter.js.map +1 -0
  29. package/dist/adapters/index.d.ts +13 -2
  30. package/dist/adapters/index.d.ts.map +1 -1
  31. package/dist/adapters/index.js +27 -5
  32. package/dist/adapters/index.js.map +1 -1
  33. package/dist/adapters/mysql-adapter.d.ts +547 -6
  34. package/dist/adapters/mysql-adapter.d.ts.map +1 -1
  35. package/dist/adapters/mysql-adapter.js +651 -32
  36. package/dist/adapters/mysql-adapter.js.map +1 -1
  37. package/dist/adapters/postgresql-adapter.d.ts +15 -4
  38. package/dist/adapters/postgresql-adapter.d.ts.map +1 -1
  39. package/dist/adapters/postgresql-adapter.js +19 -2
  40. package/dist/adapters/postgresql-adapter.js.map +1 -1
  41. package/dist/adapters/sqlite-adapter.d.ts +35 -5
  42. package/dist/adapters/sqlite-adapter.d.ts.map +1 -1
  43. package/dist/adapters/sqlite-adapter.js +57 -18
  44. package/dist/adapters/sqlite-adapter.js.map +1 -1
  45. package/dist/cli/db-dump.d.ts +32 -0
  46. package/dist/cli/db-dump.d.ts.map +1 -0
  47. package/dist/cli/db-dump.js +409 -0
  48. package/dist/cli/db-dump.js.map +1 -0
  49. package/dist/cli.js +24 -14
  50. package/dist/cli.js.map +1 -1
  51. package/dist/config/knex/bootstrap/20251025020452_create_master_tables.d.ts.map +1 -0
  52. package/dist/{migrations → config}/knex/bootstrap/20251025020452_create_master_tables.js +7 -2
  53. package/dist/config/knex/bootstrap/20251025020452_create_master_tables.js.map +1 -0
  54. package/dist/config/knex/bootstrap/20251025021152_create_transaction_tables.d.ts.map +1 -0
  55. package/dist/{migrations → config}/knex/bootstrap/20251025021152_create_transaction_tables.js +49 -50
  56. package/dist/config/knex/bootstrap/20251025021152_create_transaction_tables.js.map +1 -0
  57. package/dist/config/knex/bootstrap/20251025021351_create_indexes.d.ts.map +1 -0
  58. package/dist/config/knex/bootstrap/20251025021351_create_indexes.js.map +1 -0
  59. package/dist/config/knex/bootstrap/20251025021416_seed_master_data.d.ts.map +1 -0
  60. package/dist/{migrations → config}/knex/bootstrap/20251025021416_seed_master_data.js +11 -6
  61. package/dist/config/knex/bootstrap/20251025021416_seed_master_data.js.map +1 -0
  62. package/dist/config/knex/bootstrap/20251025070349_create_views.d.ts.map +1 -0
  63. package/dist/{migrations → config}/knex/bootstrap/20251025070349_create_views.js +66 -14
  64. package/dist/config/knex/bootstrap/20251025070349_create_views.js.map +1 -0
  65. package/dist/config/knex/enhancements/20251025081221_add_link_type_to_task_decision_links.d.ts.map +1 -0
  66. package/dist/config/knex/enhancements/20251025081221_add_link_type_to_task_decision_links.js +22 -0
  67. package/dist/config/knex/enhancements/20251025081221_add_link_type_to_task_decision_links.js.map +1 -0
  68. package/dist/config/knex/enhancements/20251025082220_fix_task_dependencies_columns.d.ts.map +1 -0
  69. package/dist/config/knex/enhancements/20251025082220_fix_task_dependencies_columns.js.map +1 -0
  70. package/dist/config/knex/enhancements/20251025090000_create_help_system_tables.d.ts.map +1 -0
  71. package/dist/{migrations → config}/knex/enhancements/20251025090000_create_help_system_tables.js +6 -0
  72. package/dist/config/knex/enhancements/20251025090000_create_help_system_tables.js.map +1 -0
  73. package/dist/config/knex/enhancements/20251025090100_seed_help_categories_and_use_cases.d.ts.map +1 -0
  74. package/dist/{migrations → config}/knex/enhancements/20251025090100_seed_help_categories_and_use_cases.js +6 -0
  75. package/dist/config/knex/enhancements/20251025090100_seed_help_categories_and_use_cases.js.map +1 -0
  76. package/dist/config/knex/enhancements/20251025100000_seed_help_metadata.d.ts.map +1 -0
  77. package/dist/{migrations → config}/knex/enhancements/20251025100000_seed_help_metadata.js +6 -0
  78. package/dist/config/knex/enhancements/20251025100000_seed_help_metadata.js.map +1 -0
  79. package/dist/config/knex/enhancements/20251025100100_seed_remaining_use_cases.d.ts.map +1 -0
  80. package/dist/config/knex/enhancements/20251025100100_seed_remaining_use_cases.js.map +1 -0
  81. package/dist/config/knex/enhancements/20251025120000_add_cascade_to_task_dependencies.d.ts.map +1 -0
  82. package/dist/{migrations → config}/knex/enhancements/20251025120000_add_cascade_to_task_dependencies.js +7 -0
  83. package/dist/config/knex/enhancements/20251025120000_add_cascade_to_task_dependencies.js.map +1 -0
  84. package/dist/config/knex/enhancements/20251027000000_add_agent_reuse_system.d.ts.map +1 -0
  85. package/dist/config/knex/enhancements/20251027000000_add_agent_reuse_system.js +62 -0
  86. package/dist/config/knex/enhancements/20251027000000_add_agent_reuse_system.js.map +1 -0
  87. package/dist/config/knex/enhancements/20251027010000_add_task_constraint_to_decision_context.d.ts.map +1 -0
  88. package/dist/config/knex/enhancements/20251027010000_add_task_constraint_to_decision_context.js.map +1 -0
  89. package/dist/config/knex/enhancements/20251027020000_update_agent_reusability.d.ts.map +1 -0
  90. package/dist/{migrations → config}/knex/enhancements/20251027020000_update_agent_reusability.js +6 -0
  91. package/dist/config/knex/enhancements/20251027020000_update_agent_reusability.js.map +1 -0
  92. package/dist/config/knex/enhancements/20251028000000_simplify_agent_system.d.ts.map +1 -0
  93. package/dist/{migrations → config}/knex/enhancements/20251028000000_simplify_agent_system.js +6 -0
  94. package/dist/config/knex/enhancements/20251028000000_simplify_agent_system.js.map +1 -0
  95. package/dist/config/knex/enhancements/20251031000000_drop_orphaned_message_view.d.ts +13 -0
  96. package/dist/config/knex/enhancements/20251031000000_drop_orphaned_message_view.d.ts.map +1 -0
  97. package/dist/config/knex/enhancements/20251031000000_drop_orphaned_message_view.js +48 -0
  98. package/dist/config/knex/enhancements/20251031000000_drop_orphaned_message_view.js.map +1 -0
  99. package/dist/config/knex/enhancements/20251104000003_rename_constraints_created_by_to_agent_id.d.ts +24 -0
  100. package/dist/config/knex/enhancements/20251104000003_rename_constraints_created_by_to_agent_id.d.ts.map +1 -0
  101. package/dist/config/knex/enhancements/20251104000003_rename_constraints_created_by_to_agent_id.js +189 -0
  102. package/dist/config/knex/enhancements/20251104000003_rename_constraints_created_by_to_agent_id.js.map +1 -0
  103. package/dist/config/knex/enhancements/20251105000000_add_token_usage_table.d.ts +16 -0
  104. package/dist/config/knex/enhancements/20251105000000_add_token_usage_table.d.ts.map +1 -0
  105. package/dist/config/knex/enhancements/20251105000000_add_token_usage_table.js +65 -0
  106. package/dist/config/knex/enhancements/20251105000000_add_token_usage_table.js.map +1 -0
  107. package/dist/config/knex/enhancements/20251105000001_rename_decision_context_decided_by_to_agent_id.d.ts +23 -0
  108. package/dist/config/knex/enhancements/20251105000001_rename_decision_context_decided_by_to_agent_id.d.ts.map +1 -0
  109. package/dist/config/knex/enhancements/20251105000001_rename_decision_context_decided_by_to_agent_id.js +118 -0
  110. package/dist/config/knex/enhancements/20251105000001_rename_decision_context_decided_by_to_agent_id.js.map +1 -0
  111. package/dist/config/knex/upgrades/20251024010000_upgrade_v1_0_to_v1_1.d.ts.map +1 -0
  112. package/dist/config/knex/upgrades/20251024010000_upgrade_v1_0_to_v1_1.js.map +1 -0
  113. package/dist/config/knex/upgrades/20251024020000_upgrade_v2_0_to_v2_1.d.ts.map +1 -0
  114. package/dist/config/knex/upgrades/20251024020000_upgrade_v2_0_to_v2_1.js.map +1 -0
  115. package/dist/config/knex/upgrades/20251024030000_upgrade_v2_1_to_v3_0.d.ts.map +1 -0
  116. package/dist/config/knex/upgrades/20251024030000_upgrade_v2_1_to_v3_0.js.map +1 -0
  117. package/dist/config/knex/upgrades/20251024040000_upgrade_v3_0_to_v3_2.d.ts.map +1 -0
  118. package/dist/config/knex/upgrades/20251024040000_upgrade_v3_0_to_v3_2.js.map +1 -0
  119. package/dist/config/knex/upgrades/20251024050000_upgrade_v3_2_0_to_v3_2_2.d.ts.map +1 -0
  120. package/dist/config/knex/upgrades/20251024050000_upgrade_v3_2_0_to_v3_2_2.js.map +1 -0
  121. package/dist/config/knex/upgrades/20251024060000_upgrade_v3_4_to_v3_5.d.ts.map +1 -0
  122. package/dist/config/knex/upgrades/20251024060000_upgrade_v3_4_to_v3_5.js.map +1 -0
  123. package/dist/config/knex/upgrades/20251024070000_upgrade_v3_5_to_v3_6.d.ts.map +1 -0
  124. package/dist/config/knex/upgrades/20251024070000_upgrade_v3_5_to_v3_6.js.map +1 -0
  125. package/dist/config/knex/upgrades/20251104000000_add_multi_project_v3_7_0.d.ts +49 -0
  126. package/dist/config/knex/upgrades/20251104000000_add_multi_project_v3_7_0.d.ts.map +1 -0
  127. package/dist/config/knex/upgrades/20251104000000_add_multi_project_v3_7_0.js +864 -0
  128. package/dist/config/knex/upgrades/20251104000000_add_multi_project_v3_7_0.js.map +1 -0
  129. package/dist/config/loader.d.ts +19 -1
  130. package/dist/config/loader.d.ts.map +1 -1
  131. package/dist/config/loader.js +149 -4
  132. package/dist/config/loader.js.map +1 -1
  133. package/dist/config/types.d.ts +261 -2
  134. package/dist/config/types.d.ts.map +1 -1
  135. package/dist/config/types.js.map +1 -1
  136. package/dist/config/writer.d.ts +65 -0
  137. package/dist/config/writer.d.ts.map +1 -0
  138. package/dist/config/writer.js +139 -0
  139. package/dist/config/writer.js.map +1 -0
  140. package/dist/database.d.ts +11 -2
  141. package/dist/database.d.ts.map +1 -1
  142. package/dist/database.js +62 -6
  143. package/dist/database.js.map +1 -1
  144. package/dist/index.js +165 -35
  145. package/dist/index.js.map +1 -1
  146. package/dist/knexfile.d.ts.map +1 -1
  147. package/dist/knexfile.js +88 -12
  148. package/dist/knexfile.js.map +1 -1
  149. package/dist/tests/all-features.test.js +15 -3
  150. package/dist/tests/all-features.test.js.map +1 -1
  151. package/dist/tests/config-loader.test.d.ts +6 -0
  152. package/dist/tests/config-loader.test.d.ts.map +1 -0
  153. package/dist/tests/config-loader.test.js +201 -0
  154. package/dist/tests/config-loader.test.js.map +1 -0
  155. package/dist/tests/connection-manager-integration.test.d.ts +2 -0
  156. package/dist/tests/connection-manager-integration.test.d.ts.map +1 -0
  157. package/dist/tests/connection-manager-integration.test.js +431 -0
  158. package/dist/tests/connection-manager-integration.test.js.map +1 -0
  159. package/dist/tests/connection-manager.test.d.ts +2 -0
  160. package/dist/tests/connection-manager.test.d.ts.map +1 -0
  161. package/dist/tests/connection-manager.test.js +361 -0
  162. package/dist/tests/connection-manager.test.js.map +1 -0
  163. package/dist/tests/dump-import.test.d.ts +15 -0
  164. package/dist/tests/dump-import.test.d.ts.map +1 -0
  165. package/dist/tests/dump-import.test.js +430 -0
  166. package/dist/tests/dump-import.test.js.map +1 -0
  167. package/dist/tests/migration-idempotency.test.d.ts +2 -0
  168. package/dist/tests/migration-idempotency.test.d.ts.map +1 -0
  169. package/dist/tests/migration-idempotency.test.js +330 -0
  170. package/dist/tests/migration-idempotency.test.js.map +1 -0
  171. package/dist/tests/migration-upgrade-paths.test.d.ts +2 -0
  172. package/dist/tests/migration-upgrade-paths.test.d.ts.map +1 -0
  173. package/dist/tests/migration-upgrade-paths.test.js +248 -0
  174. package/dist/tests/migration-upgrade-paths.test.js.map +1 -0
  175. package/dist/tests/multi-project-migration.test.d.ts +17 -0
  176. package/dist/tests/multi-project-migration.test.d.ts.map +1 -0
  177. package/dist/tests/multi-project-migration.test.js +399 -0
  178. package/dist/tests/multi-project-migration.test.js.map +1 -0
  179. package/dist/tests/multi-project.test.d.ts +5 -0
  180. package/dist/tests/multi-project.test.d.ts.map +1 -0
  181. package/dist/tests/multi-project.test.js +238 -0
  182. package/dist/tests/multi-project.test.js.map +1 -0
  183. package/dist/tests/schema-migration.test.d.ts +8 -0
  184. package/dist/tests/schema-migration.test.d.ts.map +1 -0
  185. package/dist/tests/schema-migration.test.js +108 -0
  186. package/dist/tests/schema-migration.test.js.map +1 -0
  187. package/dist/tests/sql-dump-converters.test.d.ts +7 -0
  188. package/dist/tests/sql-dump-converters.test.d.ts.map +1 -0
  189. package/dist/tests/sql-dump-converters.test.js +314 -0
  190. package/dist/tests/sql-dump-converters.test.js.map +1 -0
  191. package/dist/tests/sql-dump-cross-database.test.d.ts +21 -0
  192. package/dist/tests/sql-dump-cross-database.test.d.ts.map +1 -0
  193. package/dist/tests/sql-dump-cross-database.test.js +314 -0
  194. package/dist/tests/sql-dump-cross-database.test.js.map +1 -0
  195. package/dist/tests/sql-dump-default-conversions.test.d.ts +8 -0
  196. package/dist/tests/sql-dump-default-conversions.test.d.ts.map +1 -0
  197. package/dist/tests/sql-dump-default-conversions.test.js +141 -0
  198. package/dist/tests/sql-dump-default-conversions.test.js.map +1 -0
  199. package/dist/tests/sql-dump-fk-constraints.test.d.ts +13 -0
  200. package/dist/tests/sql-dump-fk-constraints.test.d.ts.map +1 -0
  201. package/dist/tests/sql-dump-fk-constraints.test.js +381 -0
  202. package/dist/tests/sql-dump-fk-constraints.test.js.map +1 -0
  203. package/dist/tests/sql-dump-indexes.test.d.ts +12 -0
  204. package/dist/tests/sql-dump-indexes.test.d.ts.map +1 -0
  205. package/dist/tests/sql-dump-indexes.test.js +269 -0
  206. package/dist/tests/sql-dump-indexes.test.js.map +1 -0
  207. package/dist/tests/sql-dump-integration.test.d.ts +16 -0
  208. package/dist/tests/sql-dump-integration.test.d.ts.map +1 -0
  209. package/dist/tests/sql-dump-integration.test.js +342 -0
  210. package/dist/tests/sql-dump-integration.test.js.map +1 -0
  211. package/dist/tests/sql-dump-table-ordering.test.d.ts +8 -0
  212. package/dist/tests/sql-dump-table-ordering.test.d.ts.map +1 -0
  213. package/dist/tests/sql-dump-table-ordering.test.js +253 -0
  214. package/dist/tests/sql-dump-table-ordering.test.js.map +1 -0
  215. package/dist/tests/tasks.link-file-backward-compat.test.js +11 -1
  216. package/dist/tests/tasks.link-file-backward-compat.test.js.map +1 -1
  217. package/dist/tests/tasks.watch-files-action.test.js +11 -1
  218. package/dist/tests/tasks.watch-files-action.test.js.map +1 -1
  219. package/dist/tests/type-conversion.test.d.ts +8 -0
  220. package/dist/tests/type-conversion.test.d.ts.map +1 -0
  221. package/dist/tests/type-conversion.test.js +312 -0
  222. package/dist/tests/type-conversion.test.js.map +1 -0
  223. package/dist/tests/utils/test-helpers.d.ts +93 -0
  224. package/dist/tests/utils/test-helpers.d.ts.map +1 -0
  225. package/dist/tests/utils/test-helpers.js +407 -0
  226. package/dist/tests/utils/test-helpers.js.map +1 -0
  227. package/dist/tools/config.d.ts +58 -0
  228. package/dist/tools/config.d.ts.map +1 -0
  229. package/dist/tools/config.js +281 -0
  230. package/dist/tools/config.js.map +1 -0
  231. package/dist/tools/constraints.d.ts.map +1 -1
  232. package/dist/tools/constraints.js +138 -122
  233. package/dist/tools/constraints.js.map +1 -1
  234. package/dist/tools/context.d.ts.map +1 -1
  235. package/dist/tools/context.js +216 -109
  236. package/dist/tools/context.js.map +1 -1
  237. package/dist/tools/files.d.ts.map +1 -1
  238. package/dist/tools/files.js +123 -102
  239. package/dist/tools/files.js.map +1 -1
  240. package/dist/tools/tasks.d.ts.map +1 -1
  241. package/dist/tools/tasks.js +581 -518
  242. package/dist/tools/tasks.js.map +1 -1
  243. package/dist/tools/utils.d.ts +5 -0
  244. package/dist/tools/utils.d.ts.map +1 -1
  245. package/dist/tools/utils.js +176 -122
  246. package/dist/tools/utils.js.map +1 -1
  247. package/dist/types.d.ts +9 -26
  248. package/dist/types.d.ts.map +1 -1
  249. package/dist/utils/cleanup.d.ts +3 -0
  250. package/dist/utils/cleanup.d.ts.map +1 -1
  251. package/dist/utils/cleanup.js +14 -2
  252. package/dist/utils/cleanup.js.map +1 -1
  253. package/dist/utils/connection-manager.d.ts +59 -0
  254. package/dist/utils/connection-manager.d.ts.map +1 -0
  255. package/dist/utils/connection-manager.js +178 -0
  256. package/dist/utils/connection-manager.js.map +1 -0
  257. package/dist/utils/debug-logger.d.ts +8 -4
  258. package/dist/utils/debug-logger.d.ts.map +1 -1
  259. package/dist/utils/debug-logger.js +27 -7
  260. package/dist/utils/debug-logger.js.map +1 -1
  261. package/dist/utils/error-handler.d.ts +2 -2
  262. package/dist/utils/error-handler.d.ts.map +1 -1
  263. package/dist/utils/error-handler.js +10 -7
  264. package/dist/utils/error-handler.js.map +1 -1
  265. package/dist/utils/parameter-validator.d.ts.map +1 -1
  266. package/dist/utils/parameter-validator.js +36 -15
  267. package/dist/utils/parameter-validator.js.map +1 -1
  268. package/dist/utils/project-context.d.ts +111 -0
  269. package/dist/utils/project-context.d.ts.map +1 -0
  270. package/dist/utils/project-context.js +187 -0
  271. package/dist/utils/project-context.js.map +1 -0
  272. package/dist/utils/sql-dump-converters.d.ts +188 -0
  273. package/dist/utils/sql-dump-converters.d.ts.map +1 -0
  274. package/dist/utils/sql-dump-converters.js +311 -0
  275. package/dist/utils/sql-dump-converters.js.map +1 -0
  276. package/dist/utils/sql-dump.d.ts +102 -0
  277. package/dist/utils/sql-dump.d.ts.map +1 -0
  278. package/dist/utils/sql-dump.js +1550 -0
  279. package/dist/utils/sql-dump.js.map +1 -0
  280. package/dist/utils/vcs-adapter.d.ts +42 -0
  281. package/dist/utils/vcs-adapter.d.ts.map +1 -1
  282. package/dist/utils/vcs-adapter.js +154 -0
  283. package/dist/utils/vcs-adapter.js.map +1 -1
  284. package/docs/BASEADAPTER_IMPLEMENTATION.md +399 -0
  285. package/docs/DATABASE_AUTH.md +445 -0
  286. package/docs/DATABASE_MIGRATION.md +247 -0
  287. package/docs/MULTI_PROJECT_ARCHITECTURE.md +497 -0
  288. package/package.json +12 -4
  289. package/dist/migrations/knex/bootstrap/20251025020452_create_master_tables.d.ts.map +0 -1
  290. package/dist/migrations/knex/bootstrap/20251025020452_create_master_tables.js.map +0 -1
  291. package/dist/migrations/knex/bootstrap/20251025021152_create_transaction_tables.d.ts.map +0 -1
  292. package/dist/migrations/knex/bootstrap/20251025021152_create_transaction_tables.js.map +0 -1
  293. package/dist/migrations/knex/bootstrap/20251025021351_create_indexes.d.ts.map +0 -1
  294. package/dist/migrations/knex/bootstrap/20251025021351_create_indexes.js.map +0 -1
  295. package/dist/migrations/knex/bootstrap/20251025021416_seed_master_data.d.ts.map +0 -1
  296. package/dist/migrations/knex/bootstrap/20251025021416_seed_master_data.js.map +0 -1
  297. package/dist/migrations/knex/bootstrap/20251025070349_create_views.d.ts.map +0 -1
  298. package/dist/migrations/knex/bootstrap/20251025070349_create_views.js.map +0 -1
  299. package/dist/migrations/knex/enhancements/20251025081221_add_link_type_to_task_decision_links.d.ts.map +0 -1
  300. package/dist/migrations/knex/enhancements/20251025081221_add_link_type_to_task_decision_links.js +0 -15
  301. package/dist/migrations/knex/enhancements/20251025081221_add_link_type_to_task_decision_links.js.map +0 -1
  302. package/dist/migrations/knex/enhancements/20251025082220_fix_task_dependencies_columns.d.ts.map +0 -1
  303. package/dist/migrations/knex/enhancements/20251025082220_fix_task_dependencies_columns.js.map +0 -1
  304. package/dist/migrations/knex/enhancements/20251025090000_create_help_system_tables.d.ts.map +0 -1
  305. package/dist/migrations/knex/enhancements/20251025090000_create_help_system_tables.js.map +0 -1
  306. package/dist/migrations/knex/enhancements/20251025090100_seed_help_categories_and_use_cases.d.ts.map +0 -1
  307. package/dist/migrations/knex/enhancements/20251025090100_seed_help_categories_and_use_cases.js.map +0 -1
  308. package/dist/migrations/knex/enhancements/20251025100000_seed_help_metadata.d.ts.map +0 -1
  309. package/dist/migrations/knex/enhancements/20251025100000_seed_help_metadata.js.map +0 -1
  310. package/dist/migrations/knex/enhancements/20251025100100_seed_remaining_use_cases.d.ts.map +0 -1
  311. package/dist/migrations/knex/enhancements/20251025100100_seed_remaining_use_cases.js.map +0 -1
  312. package/dist/migrations/knex/enhancements/20251025120000_add_cascade_to_task_dependencies.d.ts.map +0 -1
  313. package/dist/migrations/knex/enhancements/20251025120000_add_cascade_to_task_dependencies.js.map +0 -1
  314. package/dist/migrations/knex/enhancements/20251027000000_add_agent_reuse_system.d.ts.map +0 -1
  315. package/dist/migrations/knex/enhancements/20251027000000_add_agent_reuse_system.js +0 -34
  316. package/dist/migrations/knex/enhancements/20251027000000_add_agent_reuse_system.js.map +0 -1
  317. package/dist/migrations/knex/enhancements/20251027010000_add_task_constraint_to_decision_context.d.ts.map +0 -1
  318. package/dist/migrations/knex/enhancements/20251027010000_add_task_constraint_to_decision_context.js.map +0 -1
  319. package/dist/migrations/knex/enhancements/20251027020000_update_agent_reusability.d.ts.map +0 -1
  320. package/dist/migrations/knex/enhancements/20251027020000_update_agent_reusability.js.map +0 -1
  321. package/dist/migrations/knex/enhancements/20251028000000_simplify_agent_system.d.ts.map +0 -1
  322. package/dist/migrations/knex/enhancements/20251028000000_simplify_agent_system.js.map +0 -1
  323. package/dist/migrations/knex/upgrades/20251024010000_upgrade_v1_0_to_v1_1.d.ts.map +0 -1
  324. package/dist/migrations/knex/upgrades/20251024010000_upgrade_v1_0_to_v1_1.js.map +0 -1
  325. package/dist/migrations/knex/upgrades/20251024020000_upgrade_v2_0_to_v2_1.d.ts.map +0 -1
  326. package/dist/migrations/knex/upgrades/20251024020000_upgrade_v2_0_to_v2_1.js.map +0 -1
  327. package/dist/migrations/knex/upgrades/20251024030000_upgrade_v2_1_to_v3_0.d.ts.map +0 -1
  328. package/dist/migrations/knex/upgrades/20251024030000_upgrade_v2_1_to_v3_0.js.map +0 -1
  329. package/dist/migrations/knex/upgrades/20251024040000_upgrade_v3_0_to_v3_2.d.ts.map +0 -1
  330. package/dist/migrations/knex/upgrades/20251024040000_upgrade_v3_0_to_v3_2.js.map +0 -1
  331. package/dist/migrations/knex/upgrades/20251024050000_upgrade_v3_2_0_to_v3_2_2.d.ts.map +0 -1
  332. package/dist/migrations/knex/upgrades/20251024050000_upgrade_v3_2_0_to_v3_2_2.js.map +0 -1
  333. package/dist/migrations/knex/upgrades/20251024060000_upgrade_v3_4_to_v3_5.d.ts.map +0 -1
  334. package/dist/migrations/knex/upgrades/20251024060000_upgrade_v3_4_to_v3_5.js.map +0 -1
  335. package/dist/migrations/knex/upgrades/20251024070000_upgrade_v3_5_to_v3_6.d.ts.map +0 -1
  336. package/dist/migrations/knex/upgrades/20251024070000_upgrade_v3_5_to_v3_6.js.map +0 -1
  337. /package/dist/{migrations → config}/knex/bootstrap/20251025020452_create_master_tables.d.ts +0 -0
  338. /package/dist/{migrations → config}/knex/bootstrap/20251025021152_create_transaction_tables.d.ts +0 -0
  339. /package/dist/{migrations → config}/knex/bootstrap/20251025021351_create_indexes.d.ts +0 -0
  340. /package/dist/{migrations → config}/knex/bootstrap/20251025021351_create_indexes.js +0 -0
  341. /package/dist/{migrations → config}/knex/bootstrap/20251025021416_seed_master_data.d.ts +0 -0
  342. /package/dist/{migrations → config}/knex/bootstrap/20251025070349_create_views.d.ts +0 -0
  343. /package/dist/{migrations → config}/knex/enhancements/20251025081221_add_link_type_to_task_decision_links.d.ts +0 -0
  344. /package/dist/{migrations → config}/knex/enhancements/20251025082220_fix_task_dependencies_columns.d.ts +0 -0
  345. /package/dist/{migrations → config}/knex/enhancements/20251025082220_fix_task_dependencies_columns.js +0 -0
  346. /package/dist/{migrations → config}/knex/enhancements/20251025090000_create_help_system_tables.d.ts +0 -0
  347. /package/dist/{migrations → config}/knex/enhancements/20251025090100_seed_help_categories_and_use_cases.d.ts +0 -0
  348. /package/dist/{migrations → config}/knex/enhancements/20251025100000_seed_help_metadata.d.ts +0 -0
  349. /package/dist/{migrations → config}/knex/enhancements/20251025100100_seed_remaining_use_cases.d.ts +0 -0
  350. /package/dist/{migrations → config}/knex/enhancements/20251025100100_seed_remaining_use_cases.js +0 -0
  351. /package/dist/{migrations → config}/knex/enhancements/20251025120000_add_cascade_to_task_dependencies.d.ts +0 -0
  352. /package/dist/{migrations → config}/knex/enhancements/20251027000000_add_agent_reuse_system.d.ts +0 -0
  353. /package/dist/{migrations → config}/knex/enhancements/20251027010000_add_task_constraint_to_decision_context.d.ts +0 -0
  354. /package/dist/{migrations → config}/knex/enhancements/20251027010000_add_task_constraint_to_decision_context.js +0 -0
  355. /package/dist/{migrations → config}/knex/enhancements/20251027020000_update_agent_reusability.d.ts +0 -0
  356. /package/dist/{migrations → config}/knex/enhancements/20251028000000_simplify_agent_system.d.ts +0 -0
  357. /package/dist/{migrations → config}/knex/upgrades/20251024010000_upgrade_v1_0_to_v1_1.d.ts +0 -0
  358. /package/dist/{migrations → config}/knex/upgrades/20251024010000_upgrade_v1_0_to_v1_1.js +0 -0
  359. /package/dist/{migrations → config}/knex/upgrades/20251024020000_upgrade_v2_0_to_v2_1.d.ts +0 -0
  360. /package/dist/{migrations → config}/knex/upgrades/20251024020000_upgrade_v2_0_to_v2_1.js +0 -0
  361. /package/dist/{migrations → config}/knex/upgrades/20251024030000_upgrade_v2_1_to_v3_0.d.ts +0 -0
  362. /package/dist/{migrations → config}/knex/upgrades/20251024030000_upgrade_v2_1_to_v3_0.js +0 -0
  363. /package/dist/{migrations → config}/knex/upgrades/20251024040000_upgrade_v3_0_to_v3_2.d.ts +0 -0
  364. /package/dist/{migrations → config}/knex/upgrades/20251024040000_upgrade_v3_0_to_v3_2.js +0 -0
  365. /package/dist/{migrations → config}/knex/upgrades/20251024050000_upgrade_v3_2_0_to_v3_2_2.d.ts +0 -0
  366. /package/dist/{migrations → config}/knex/upgrades/20251024050000_upgrade_v3_2_0_to_v3_2_2.js +0 -0
  367. /package/dist/{migrations → config}/knex/upgrades/20251024060000_upgrade_v3_4_to_v3_5.d.ts +0 -0
  368. /package/dist/{migrations → config}/knex/upgrades/20251024060000_upgrade_v3_4_to_v3_5.js +0 -0
  369. /package/dist/{migrations → config}/knex/upgrades/20251024070000_upgrade_v3_5_to_v3_6.d.ts +0 -0
  370. /package/dist/{migrations → config}/knex/upgrades/20251024070000_upgrade_v3_5_to_v3_6.js +0 -0
@@ -0,0 +1,864 @@
1
+ /**
2
+ * Migration: Multi-Project Support v3.7.0 (Consolidated)
3
+ *
4
+ * Consolidates 4 separate migrations into a single comprehensive migration:
5
+ * - 20251101000000_add_multi_project_support (enhancements)
6
+ * - 20251104000000_multi_project_support_v3_7_0 (upgrades)
7
+ * - 20251104000001_multi_project_fix_constraints (upgrades)
8
+ * - 20251104000002_hotfix_missing_project_id (upgrades)
9
+ *
10
+ * Adds multi-project isolation support by:
11
+ * 1. Creating m_projects master table
12
+ * 2. Adding project_id to all transaction tables (14 tables)
13
+ * 3. Updating PRIMARY KEY constraints to composite (key_id, project_id)
14
+ * 4. Recreating m_config table with proper PRIMARY KEY structure
15
+ * 5. Adding indexes for multi-project queries
16
+ * 6. Recreating all views with project_id support
17
+ *
18
+ * Migration Steps:
19
+ * - STEP 1: Create m_projects table
20
+ * - STEP 2: Drop all views and triggers before modifications
21
+ * - STEP 3: Add project_id via ALTER TABLE (10 tables)
22
+ * - STEP 4: Fix PRIMARY KEY constraints (composite keys)
23
+ * - STEP 4a-4b: Recreate t_decisions tables with composite PRIMARY KEY
24
+ * - STEP 4.5: Recreate t_task_tags with composite PRIMARY KEY
25
+ * - STEP 4.6: Recreate t_task_dependencies with composite PRIMARY KEY
26
+ * - STEP 4.7: Recreate t_task_details with project_id (ALTER TABLE fails due to FK constraints)
27
+ * - STEP 4.8: Recreate t_task_file_links with project_id (ALTER TABLE fails due to FK constraints)
28
+ * - STEP 4.9: Recreate t_task_decision_links with project_id (ALTER TABLE fails due to FK constraints)
29
+ * - STEP 5: Recreate m_config table
30
+ * - STEP 6: Create composite indexes
31
+ * - STEP 7: Recreate all views with project_id support
32
+ * - STEP 8: Re-enable foreign key constraints
33
+ *
34
+ * SQLite Limitation Note:
35
+ * ALTER TABLE cannot modify tables with complex FOREIGN KEY constraints (ON DELETE CASCADE).
36
+ * Tables requiring recreation: t_task_details, t_task_tags, t_task_file_links,
37
+ * t_task_decision_links, t_task_dependencies.
38
+ *
39
+ * Satisfies Constraints:
40
+ * - #22 (CRITICAL): All transaction tables have project_id
41
+ * - #23 (CRITICAL): m_projects table with name/detection_source
42
+ * - #39 (HIGH): Composite indexes with project_id first
43
+ * - #41 (HIGH): t_task_tags composite PRIMARY KEY
44
+ * - #42 (HIGH): t_task_dependencies composite PRIMARY KEY
45
+ */
46
+ export async function up(knex) {
47
+ // Check if migration already completed
48
+ const hasProjectsTable = await knex.schema.hasTable('m_projects');
49
+ const hasProjectIdInDecisions = await knex.schema.hasColumn('t_decisions', 'project_id');
50
+ const hasMigrationMarker = await knex.schema.hasTable('_multi_project_pk_fixed');
51
+ console.log(`🔍 Multi-project migration check:`);
52
+ console.log(` - m_projects: ${hasProjectsTable}`);
53
+ console.log(` - project_id in t_decisions: ${hasProjectIdInDecisions}`);
54
+ console.log(` - migration marker: ${hasMigrationMarker}`);
55
+ // If fully migrated, skip
56
+ if (hasProjectsTable && hasProjectIdInDecisions && hasMigrationMarker) {
57
+ console.log('✓ Multi-project schema already migrated, skipping');
58
+ return;
59
+ }
60
+ console.log('🔄 Starting multi-project support migration v3.7.0 (consolidated)...');
61
+ // Disable foreign key constraints temporarily for SQLite
62
+ await knex.raw('PRAGMA foreign_keys = OFF');
63
+ console.log('✓ Disabled foreign key constraints');
64
+ // ============================================================================
65
+ // STEP 1: Create m_projects Master Table
66
+ // ============================================================================
67
+ let defaultProjectId;
68
+ if (!hasProjectsTable) {
69
+ await knex.schema.createTable('m_projects', (table) => {
70
+ table.increments('id').primary();
71
+ table.string('name', 64).notNullable().unique();
72
+ table.string('display_name', 128);
73
+ table.string('detection_source', 20).notNullable(); // 'cli' | 'config' | 'git' | 'metadata' | 'directory'
74
+ table.string('project_root_path', 512);
75
+ table.integer('created_ts').notNullable();
76
+ table.integer('last_active_ts').notNullable();
77
+ table.text('metadata'); // JSON string for extensibility
78
+ });
79
+ // Insert default project for existing data
80
+ const now = Math.floor(Date.now() / 1000);
81
+ await knex('m_projects').insert({
82
+ id: 1,
83
+ name: 'default-project',
84
+ display_name: 'Default Project (Migrated)',
85
+ detection_source: 'directory',
86
+ created_ts: now,
87
+ last_active_ts: now,
88
+ });
89
+ defaultProjectId = 1;
90
+ console.log(`✓ Created m_projects table with default project (ID: ${defaultProjectId})`);
91
+ }
92
+ else {
93
+ // Use existing first project as default
94
+ const firstProject = await knex('m_projects').orderBy('id').first();
95
+ defaultProjectId = firstProject?.id || 1;
96
+ console.log(`✓ Using existing project ID ${defaultProjectId} as default`);
97
+ }
98
+ // ============================================================================
99
+ // STEP 2: Drop All Views and Triggers (Before Table Modifications)
100
+ // ============================================================================
101
+ // Drop ALL views (including old schema views)
102
+ const views = await knex.raw(`SELECT name FROM sqlite_master WHERE type='view'`);
103
+ for (const view of views) {
104
+ await knex.raw(`DROP VIEW IF EXISTS ${view.name}`);
105
+ }
106
+ console.log(`✓ Dropped all ${views.length} views before table modifications`);
107
+ // Drop ALL triggers (old schema compatibility)
108
+ const triggers = await knex.raw(`SELECT name FROM sqlite_master WHERE type='trigger'`);
109
+ for (const trigger of triggers) {
110
+ await knex.raw(`DROP TRIGGER IF EXISTS ${trigger.name}`);
111
+ }
112
+ console.log(`✓ Dropped all ${triggers.length} triggers before table modifications`);
113
+ // Drop old t_agent_messages table if exists (removed in v3.6.5)
114
+ await knex.schema.dropTableIfExists('t_agent_messages');
115
+ console.log('✓ Dropped t_agent_messages if it existed (removed in v3.6.5)');
116
+ // ============================================================================
117
+ // STEP 3: Add project_id to Transaction Tables
118
+ // ============================================================================
119
+ // Helper function to add project_id column using raw SQL (more reliable for old schemas)
120
+ async function addProjectIdColumn(tableName, knex, defaultProjectId) {
121
+ const hasColumn = await knex.schema.hasColumn(tableName, 'project_id');
122
+ if (!hasColumn) {
123
+ // Use raw SQL to avoid Knex's schema parsing issues with old schemas
124
+ await knex.raw(`ALTER TABLE ${tableName} ADD COLUMN project_id INTEGER NOT NULL DEFAULT ${defaultProjectId}`);
125
+ // Add foreign key separately
126
+ await knex.raw(`CREATE INDEX IF NOT EXISTS idx_${tableName}_project ON ${tableName}(project_id)`);
127
+ console.log(`✓ Added project_id to ${tableName}`);
128
+ }
129
+ else {
130
+ console.log(` ⏭ ${tableName} already has project_id, skipping`);
131
+ }
132
+ }
133
+ // Transaction tables that need project_id
134
+ // Note: t_decision_context is handled separately in Step 4 (after PRIMARY KEY fix)
135
+ // Note: t_task_details, t_task_tags, t_task_dependencies are recreated in Steps 4.5-4.7 (need table recreation for constraints)
136
+ const transactionTables = [
137
+ 't_decision_history',
138
+ 't_decision_tags',
139
+ 't_decision_scopes',
140
+ 't_file_changes',
141
+ 't_constraints',
142
+ 't_tasks',
143
+ // 't_task_details', // Handled in STEP 4.7 (needs table recreation)
144
+ // 't_task_tags', // Handled in STEP 4.5 (composite PRIMARY KEY)
145
+ 't_task_file_links',
146
+ 't_task_decision_links',
147
+ // 't_task_dependencies', // Handled in STEP 4.6 (composite PRIMARY KEY)
148
+ 't_activity_log', // Required for stats.clear to filter by project_id
149
+ ];
150
+ for (const tableName of transactionTables) {
151
+ await addProjectIdColumn(tableName, knex, defaultProjectId);
152
+ }
153
+ // ============================================================================
154
+ // STEP 4: Fix PRIMARY KEY Constraints (t_decisions tables)
155
+ // ============================================================================
156
+ // For SQLite, we need to recreate tables to change PRIMARY KEY from single-column
157
+ // to composite (key_id, project_id)
158
+ if (!hasMigrationMarker) {
159
+ console.log('🔄 Fixing PRIMARY KEY constraints for t_decisions tables...');
160
+ // Drop t_decision_context temporarily (has FK to t_decisions)
161
+ let decisionContextData = [];
162
+ const hasDecisionContext = await knex.schema.hasTable('t_decision_context');
163
+ if (hasDecisionContext) {
164
+ decisionContextData = await knex('t_decision_context').select('*');
165
+ await knex.schema.dropTable('t_decision_context');
166
+ console.log('✓ Temporarily dropped t_decision_context (will recreate)');
167
+ }
168
+ // 4a. t_decisions
169
+ const decisionsData = await knex('t_decisions').select('*');
170
+ await knex.schema.dropTableIfExists('t_decisions');
171
+ await knex.schema.createTable('t_decisions', (table) => {
172
+ table.integer('key_id').unsigned().notNullable();
173
+ table.integer('project_id').unsigned().notNullable().defaultTo(defaultProjectId);
174
+ table.text('value').notNullable();
175
+ table.integer('agent_id').unsigned();
176
+ table.integer('layer_id').unsigned();
177
+ table.string('version', 20).defaultTo('1.0.0');
178
+ table.integer('status').defaultTo(1);
179
+ table.integer('ts').notNullable();
180
+ // Composite PRIMARY KEY
181
+ table.primary(['key_id', 'project_id']);
182
+ // Foreign keys
183
+ table.foreign('key_id').references('m_context_keys.id');
184
+ table.foreign('agent_id').references('m_agents.id');
185
+ table.foreign('layer_id').references('m_layers.id');
186
+ table.foreign('project_id').references('m_projects.id').onDelete('CASCADE');
187
+ });
188
+ if (decisionsData.length > 0) {
189
+ // Ensure project_id is set for existing data
190
+ await knex('t_decisions').insert(decisionsData.map((row) => ({
191
+ ...row,
192
+ project_id: row.project_id || defaultProjectId,
193
+ })));
194
+ }
195
+ console.log(`✓ Recreated t_decisions with composite PRIMARY KEY (${decisionsData.length} rows)`);
196
+ // 4b. t_decisions_numeric
197
+ const decisionsNumericData = await knex('t_decisions_numeric').select('*');
198
+ await knex.schema.dropTableIfExists('t_decisions_numeric');
199
+ await knex.schema.createTable('t_decisions_numeric', (table) => {
200
+ table.integer('key_id').unsigned().notNullable();
201
+ table.integer('project_id').unsigned().notNullable().defaultTo(defaultProjectId);
202
+ table.double('value').notNullable();
203
+ table.integer('agent_id').unsigned();
204
+ table.integer('layer_id').unsigned();
205
+ table.string('version', 20).defaultTo('1.0.0');
206
+ table.integer('status').defaultTo(1);
207
+ table.integer('ts').notNullable();
208
+ // Composite PRIMARY KEY
209
+ table.primary(['key_id', 'project_id']);
210
+ // Foreign keys
211
+ table.foreign('key_id').references('m_context_keys.id');
212
+ table.foreign('agent_id').references('m_agents.id');
213
+ table.foreign('layer_id').references('m_layers.id');
214
+ table.foreign('project_id').references('m_projects.id').onDelete('CASCADE');
215
+ });
216
+ if (decisionsNumericData.length > 0) {
217
+ await knex('t_decisions_numeric').insert(decisionsNumericData.map((row) => ({
218
+ ...row,
219
+ project_id: row.project_id || defaultProjectId,
220
+ })));
221
+ }
222
+ console.log(`✓ Recreated t_decisions_numeric with composite PRIMARY KEY (${decisionsNumericData.length} rows)`);
223
+ // Create migration marker
224
+ await knex.schema.createTable('_multi_project_pk_fixed', (table) => {
225
+ table.integer('applied_ts').notNullable();
226
+ });
227
+ await knex('_multi_project_pk_fixed').insert({
228
+ applied_ts: Math.floor(Date.now() / 1000),
229
+ });
230
+ console.log('✓ Created migration marker table');
231
+ // Recreate t_decision_context if it existed
232
+ if (hasDecisionContext) {
233
+ await knex.schema.createTable('t_decision_context', (table) => {
234
+ table.increments('id').primary();
235
+ table.integer('decision_key_id').unsigned().notNullable();
236
+ table.text('rationale');
237
+ table.text('alternatives_considered'); // JSON array (was 'alternatives' - fixed for compatibility)
238
+ table.text('tradeoffs'); // JSON object
239
+ table.integer('agent_id').unsigned();
240
+ table.integer('decision_date').notNullable().defaultTo(knex.raw('(unixepoch())'));
241
+ table.integer('related_task_id').unsigned();
242
+ table.integer('related_constraint_id').unsigned();
243
+ table.integer('ts').notNullable().defaultTo(knex.raw('(unixepoch())'));
244
+ table.integer('project_id').unsigned().notNullable().defaultTo(defaultProjectId);
245
+ // Foreign keys
246
+ table.foreign('decision_key_id').references('m_context_keys.id');
247
+ table.foreign('agent_id').references('m_agents.id');
248
+ table.foreign('related_task_id').references('t_tasks.id').onDelete('SET NULL');
249
+ table.foreign('related_constraint_id').references('t_constraints.id').onDelete('SET NULL');
250
+ table.foreign('project_id').references('m_projects.id').onDelete('CASCADE');
251
+ // Unique constraint
252
+ table.unique(['decision_key_id', 'id']);
253
+ });
254
+ // Restore data with project_id
255
+ if (decisionContextData.length > 0) {
256
+ await knex('t_decision_context').insert(decisionContextData.map((row) => ({
257
+ ...row,
258
+ project_id: row.project_id || defaultProjectId,
259
+ })));
260
+ }
261
+ console.log(`✓ Recreated t_decision_context (${decisionContextData.length} rows)`);
262
+ }
263
+ }
264
+ else {
265
+ console.log('✓ PRIMARY KEY constraints already fixed, skipping');
266
+ }
267
+ // ============================================================================
268
+ // STEP 4.5: Fix t_task_tags PRIMARY KEY (Constraint #41)
269
+ // ============================================================================
270
+ // t_task_tags needs composite PRIMARY KEY (project_id, task_id, tag_id)
271
+ // to support ON CONFLICT clause for multi-project tag insertion
272
+ const taskTagsHasCorrectPK = await knex.raw(`SELECT sql FROM sqlite_master WHERE type='table' AND name='t_task_tags'`).then((result) => {
273
+ const createSql = result[0]?.sql || '';
274
+ return createSql.includes('PRIMARY KEY (project_id, task_id, tag_id)') ||
275
+ createSql.includes('PRIMARY KEY(project_id, task_id, tag_id)');
276
+ });
277
+ if (!taskTagsHasCorrectPK) {
278
+ console.log('🔄 Fixing t_task_tags PRIMARY KEY to include project_id...');
279
+ // Backup existing data
280
+ const taskTagsData = await knex('t_task_tags').select('*');
281
+ // Drop and recreate table with correct PRIMARY KEY
282
+ await knex.schema.dropTableIfExists('t_task_tags');
283
+ await knex.schema.createTable('t_task_tags', (table) => {
284
+ table.integer('project_id').unsigned().notNullable().defaultTo(defaultProjectId);
285
+ table.integer('task_id').unsigned().notNullable();
286
+ table.integer('tag_id').unsigned().notNullable();
287
+ // Composite PRIMARY KEY with project_id first
288
+ table.primary(['project_id', 'task_id', 'tag_id']);
289
+ // Foreign keys
290
+ table.foreign('project_id').references('id').inTable('m_projects').onDelete('CASCADE');
291
+ table.foreign('task_id').references('id').inTable('t_tasks').onDelete('CASCADE');
292
+ table.foreign('tag_id').references('id').inTable('m_tags');
293
+ });
294
+ // Restore data with project_id
295
+ if (taskTagsData.length > 0) {
296
+ await knex('t_task_tags').insert(taskTagsData.map((row) => ({
297
+ ...row,
298
+ project_id: row.project_id || defaultProjectId,
299
+ })));
300
+ }
301
+ console.log(`✓ Recreated t_task_tags with composite PRIMARY KEY (${taskTagsData.length} rows)`);
302
+ }
303
+ else {
304
+ console.log('✓ t_task_tags PRIMARY KEY already correct, skipping');
305
+ }
306
+ // STEP 4.6: Fix t_task_dependencies PRIMARY KEY (Constraint #42)
307
+ // PRIMARY KEY must be (project_id, blocker_task_id, blocked_task_id) for multi-project support
308
+ const taskDepsHasCorrectPK = await knex.raw(`SELECT sql FROM sqlite_master WHERE type='table' AND name='t_task_dependencies'`).then((result) => {
309
+ const createSql = result[0]?.sql || '';
310
+ return createSql.includes('PRIMARY KEY (project_id, blocker_task_id, blocked_task_id)') ||
311
+ createSql.includes('PRIMARY KEY(project_id, blocker_task_id, blocked_task_id)');
312
+ });
313
+ if (!taskDepsHasCorrectPK) {
314
+ console.log('🔄 Fixing t_task_dependencies PRIMARY KEY to include project_id...');
315
+ const taskDepsData = await knex('t_task_dependencies').select('*');
316
+ await knex.schema.dropTableIfExists('t_task_dependencies');
317
+ await knex.schema.createTable('t_task_dependencies', (table) => {
318
+ table.integer('project_id').unsigned().notNullable().defaultTo(defaultProjectId);
319
+ table.integer('blocker_task_id').unsigned().notNullable();
320
+ table.integer('blocked_task_id').unsigned().notNullable();
321
+ table.integer('created_ts').notNullable();
322
+ table.primary(['project_id', 'blocker_task_id', 'blocked_task_id']);
323
+ table.foreign('project_id').references('id').inTable('m_projects').onDelete('CASCADE');
324
+ table.foreign('blocker_task_id').references('id').inTable('t_tasks').onDelete('CASCADE');
325
+ table.foreign('blocked_task_id').references('id').inTable('t_tasks').onDelete('CASCADE');
326
+ table.index('blocked_task_id', 'idx_task_deps_blocked');
327
+ });
328
+ if (taskDepsData.length > 0) {
329
+ await knex('t_task_dependencies').insert(taskDepsData.map((row) => ({
330
+ ...row,
331
+ project_id: row.project_id || defaultProjectId,
332
+ })));
333
+ }
334
+ console.log(`✓ Recreated t_task_dependencies with composite PRIMARY KEY (${taskDepsData.length} rows)`);
335
+ }
336
+ else {
337
+ console.log('✓ t_task_dependencies PRIMARY KEY already correct, skipping');
338
+ }
339
+ // ============================================================================
340
+ // STEP 4.7: Fix t_task_details - Add project_id with table recreation
341
+ // ============================================================================
342
+ // t_task_details needs project_id but ALTER TABLE may fail due to foreign key constraints
343
+ // Recreate table to ensure proper schema
344
+ const taskDetailsHasProjectId = await knex.schema.hasColumn('t_task_details', 'project_id');
345
+ if (!taskDetailsHasProjectId) {
346
+ console.log('🔄 Adding project_id to t_task_details (table recreation)...');
347
+ // Backup existing data
348
+ const taskDetailsData = await knex('t_task_details').select('*');
349
+ // Get project_id mapping from t_tasks
350
+ const tasksWithProjects = await knex('t_tasks')
351
+ .select('id', 'project_id')
352
+ .whereIn('id', taskDetailsData.map((row) => row.task_id));
353
+ const projectMap = new Map(tasksWithProjects.map((t) => [t.id, t.project_id]));
354
+ // Drop and recreate table with project_id
355
+ await knex.schema.dropTableIfExists('t_task_details');
356
+ await knex.schema.createTable('t_task_details', (table) => {
357
+ table.integer('task_id').primary();
358
+ table.integer('project_id').unsigned().notNullable().defaultTo(defaultProjectId);
359
+ table.text('description');
360
+ table.text('acceptance_criteria');
361
+ table.text('notes');
362
+ table.text('acceptance_criteria_json');
363
+ // Foreign keys
364
+ table.foreign('task_id').references('id').inTable('t_tasks').onDelete('CASCADE');
365
+ table.foreign('project_id').references('id').inTable('m_projects').onDelete('CASCADE');
366
+ // Index for project-scoped queries
367
+ table.index(['project_id', 'task_id'], 'idx_task_details_project');
368
+ });
369
+ // Restore data with project_id from parent tasks
370
+ if (taskDetailsData.length > 0) {
371
+ await knex('t_task_details').insert(taskDetailsData.map((row) => ({
372
+ ...row,
373
+ project_id: projectMap.get(row.task_id) || defaultProjectId,
374
+ })));
375
+ }
376
+ console.log(`✓ Recreated t_task_details with project_id (${taskDetailsData.length} rows)`);
377
+ }
378
+ else {
379
+ console.log('✓ t_task_details already has project_id, skipping');
380
+ }
381
+ // ============================================================================
382
+ // STEP 4.8: Fix t_task_file_links - Add project_id with table recreation
383
+ // ============================================================================
384
+ // t_task_file_links needs project_id but ALTER TABLE may fail due to foreign key constraints
385
+ // Recreate table to ensure proper schema
386
+ const taskFileLinksHasProjectId = await knex.schema.hasColumn('t_task_file_links', 'project_id');
387
+ if (!taskFileLinksHasProjectId) {
388
+ console.log('🔄 Adding project_id to t_task_file_links (table recreation)...');
389
+ // Backup existing data
390
+ const taskFileLinksData = await knex('t_task_file_links').select('*');
391
+ // Get project_id mapping from t_tasks
392
+ const tasksWithProjects2 = await knex('t_tasks')
393
+ .select('id', 'project_id')
394
+ .whereIn('id', taskFileLinksData.map((row) => row.task_id));
395
+ const projectMap2 = new Map(tasksWithProjects2.map((t) => [t.id, t.project_id]));
396
+ // Drop and recreate table with project_id
397
+ await knex.schema.dropTableIfExists('t_task_file_links');
398
+ await knex.schema.createTable('t_task_file_links', (table) => {
399
+ table.increments('id').primary();
400
+ table.integer('task_id').unsigned().notNullable();
401
+ table.integer('file_id').unsigned().notNullable();
402
+ table.integer('project_id').unsigned().notNullable().defaultTo(defaultProjectId);
403
+ table.integer('linked_ts').notNullable();
404
+ // Foreign keys
405
+ table.foreign('task_id').references('id').inTable('t_tasks').onDelete('CASCADE');
406
+ table.foreign('file_id').references('id').inTable('m_files');
407
+ table.foreign('project_id').references('id').inTable('m_projects').onDelete('CASCADE');
408
+ // Indexes
409
+ table.index('task_id', 'idx_task_file_links_task');
410
+ table.index(['project_id', 'task_id'], 'idx_task_file_links_project');
411
+ });
412
+ // Restore data with project_id from parent tasks
413
+ if (taskFileLinksData.length > 0) {
414
+ await knex('t_task_file_links').insert(taskFileLinksData.map((row) => ({
415
+ ...row,
416
+ project_id: projectMap2.get(row.task_id) || defaultProjectId,
417
+ })));
418
+ }
419
+ console.log(`✓ Recreated t_task_file_links with project_id (${taskFileLinksData.length} rows)`);
420
+ }
421
+ else {
422
+ console.log('✓ t_task_file_links already has project_id, skipping');
423
+ }
424
+ // ============================================================================
425
+ // STEP 4.9: Fix t_task_decision_links - Add project_id with table recreation
426
+ // ============================================================================
427
+ // t_task_decision_links needs project_id but ALTER TABLE may fail due to foreign key constraints
428
+ // Recreate table to ensure proper schema
429
+ const taskDecisionLinksHasProjectId = await knex.schema.hasColumn('t_task_decision_links', 'project_id');
430
+ if (!taskDecisionLinksHasProjectId) {
431
+ console.log('🔄 Adding project_id to t_task_decision_links (table recreation)...');
432
+ // Backup existing data
433
+ const taskDecisionLinksData = await knex('t_task_decision_links').select('*');
434
+ // Get project_id mapping from t_tasks
435
+ const tasksWithProjects3 = await knex('t_tasks')
436
+ .select('id', 'project_id')
437
+ .whereIn('id', taskDecisionLinksData.map((row) => row.task_id));
438
+ const projectMap3 = new Map(tasksWithProjects3.map((t) => [t.id, t.project_id]));
439
+ // Drop and recreate table with project_id
440
+ await knex.schema.dropTableIfExists('t_task_decision_links');
441
+ await knex.schema.createTable('t_task_decision_links', (table) => {
442
+ table.increments('id').primary();
443
+ table.integer('task_id').unsigned().notNullable();
444
+ table.integer('decision_key_id').unsigned().notNullable();
445
+ table.integer('project_id').unsigned().notNullable().defaultTo(defaultProjectId);
446
+ table.text('link_type').defaultTo('implements');
447
+ table.integer('linked_ts').notNullable();
448
+ // Foreign keys
449
+ table.foreign('task_id').references('id').inTable('t_tasks').onDelete('CASCADE');
450
+ table.foreign('decision_key_id').references('id').inTable('m_context_keys');
451
+ table.foreign('project_id').references('id').inTable('m_projects').onDelete('CASCADE');
452
+ // Indexes
453
+ table.index('task_id', 'idx_task_decision_links_task');
454
+ table.index('decision_key_id', 'idx_task_decision_links_decision');
455
+ table.index(['project_id', 'task_id'], 'idx_task_decision_links_project');
456
+ });
457
+ // Restore data with project_id from parent tasks
458
+ if (taskDecisionLinksData.length > 0) {
459
+ await knex('t_task_decision_links').insert(taskDecisionLinksData.map((row) => ({
460
+ ...row,
461
+ project_id: projectMap3.get(row.task_id) || defaultProjectId,
462
+ })));
463
+ }
464
+ console.log(`✓ Recreated t_task_decision_links with project_id (${taskDecisionLinksData.length} rows)`);
465
+ }
466
+ else {
467
+ console.log('✓ t_task_decision_links already has project_id, skipping');
468
+ }
469
+ // ============================================================================
470
+ // STEP 5: Recreate m_config Table
471
+ // ============================================================================
472
+ // m_config needs special handling to avoid nullable composite PRIMARY KEY
473
+ // We use a single-column PRIMARY KEY on 'key' and project_id is nullable
474
+ const configHasProjectId = await knex.schema.hasColumn('m_config', 'project_id');
475
+ if (!configHasProjectId) {
476
+ console.log('🔄 Recreating m_config table with multi-project support...');
477
+ // Drop temp table if exists from previous partial run
478
+ await knex.schema.dropTableIfExists('m_config_new');
479
+ // Create new m_config table with project_id
480
+ await knex.schema.createTable('m_config_new', (table) => {
481
+ table.string('key', 64).notNullable();
482
+ table.integer('project_id').unsigned().nullable(); // Nullable for global config
483
+ table.text('value').notNullable();
484
+ // Single-column PRIMARY KEY (key only)
485
+ table.primary(['key']);
486
+ // Foreign key with CASCADE delete
487
+ table.foreign('project_id').references('id').inTable('m_projects').onDelete('CASCADE');
488
+ // Composite index for project-scoped lookups
489
+ table.index(['project_id', 'key'], 'idx_config_project_key');
490
+ });
491
+ // Migrate existing config data
492
+ const existingConfig = await knex('m_config').select('key', 'value');
493
+ if (existingConfig.length > 0) {
494
+ await knex('m_config_new').insert(existingConfig.map((row) => ({
495
+ key: row.key,
496
+ project_id: null, // Global config has NULL project_id
497
+ value: row.value,
498
+ })));
499
+ }
500
+ // Swap tables
501
+ await knex.schema.dropTable('m_config');
502
+ await knex.schema.renameTable('m_config_new', 'm_config');
503
+ console.log(`✓ Recreated m_config table (${existingConfig.length} rows migrated)`);
504
+ }
505
+ else {
506
+ console.log('✓ m_config already has project_id, skipping');
507
+ }
508
+ // ============================================================================
509
+ // STEP 6: Create Composite Indexes (Constraint #39)
510
+ // ============================================================================
511
+ // All multi-project indexes should have project_id first for optimal performance
512
+ const indexes = [
513
+ { table: 't_decisions', columns: ['project_id', 'key_id'], name: 'idx_decisions_project_key' },
514
+ { table: 't_decisions', columns: ['project_id', 'ts'], name: 'idx_decisions_project_ts' },
515
+ { table: 't_decisions_numeric', columns: ['project_id', 'key_id'], name: 'idx_decisions_numeric_project_key' },
516
+ { table: 't_decision_tags', columns: ['project_id', 'decision_key_id'], name: 'idx_decision_tags_project_key' },
517
+ { table: 't_tasks', columns: ['project_id', 'status_id'], name: 'idx_tasks_project_status' },
518
+ { table: 't_tasks', columns: ['project_id', 'created_ts'], name: 'idx_tasks_project_created' },
519
+ ];
520
+ for (const { table, columns, name } of indexes) {
521
+ try {
522
+ // Check if index already exists
523
+ const indexCheck = await knex.raw(`SELECT name FROM sqlite_master WHERE type='index' AND name=?`, [name]);
524
+ if (!indexCheck || indexCheck.length === 0) {
525
+ await knex.schema.alterTable(table, (tbl) => {
526
+ tbl.index(columns, name);
527
+ });
528
+ console.log(`✓ Created index ${name}`);
529
+ }
530
+ else {
531
+ console.log(` ⏭ Index ${name} already exists, skipping`);
532
+ }
533
+ }
534
+ catch (error) {
535
+ // Ignore duplicate index errors
536
+ if (!error.message.includes('already exists')) {
537
+ throw error;
538
+ }
539
+ console.log(` ⏭ Index ${name} already exists, skipping`);
540
+ }
541
+ }
542
+ // ============================================================================
543
+ // STEP 7: Recreate All Views with Multi-Project Support
544
+ // ============================================================================
545
+ console.log('🔄 Recreating views with multi-project support...');
546
+ // v_tagged_decisions - Must match bootstrap schema (20251025070349_create_views.ts)
547
+ await knex.raw(`
548
+ CREATE VIEW v_tagged_decisions AS
549
+ SELECT
550
+ k.key,
551
+ d.value,
552
+ d.version,
553
+ CASE d.status WHEN 1 THEN 'active' WHEN 2 THEN 'deprecated' ELSE 'draft' END as status,
554
+ d.project_id,
555
+ l.name as layer,
556
+ (SELECT GROUP_CONCAT(t2.name, ',') FROM t_decision_tags dt2
557
+ JOIN m_tags t2 ON dt2.tag_id = t2.id
558
+ WHERE dt2.decision_key_id = d.key_id AND dt2.project_id = d.project_id) as tags,
559
+ (SELECT GROUP_CONCAT(s2.name, ',') FROM t_decision_scopes ds2
560
+ JOIN m_scopes s2 ON ds2.scope_id = s2.id
561
+ WHERE ds2.decision_key_id = d.key_id AND ds2.project_id = d.project_id) as scopes,
562
+ a.name as decided_by,
563
+ datetime(d.ts, 'unixepoch') as updated
564
+ FROM t_decisions d
565
+ JOIN m_context_keys k ON d.key_id = k.id
566
+ LEFT JOIN m_layers l ON d.layer_id = l.id
567
+ LEFT JOIN m_agents a ON d.agent_id = a.id
568
+ `);
569
+ // v_active_context
570
+ await knex.raw(`
571
+ CREATE VIEW v_active_context AS
572
+ SELECT
573
+ k.key,
574
+ d.value,
575
+ d.project_id,
576
+ l.name as layer,
577
+ a.name as decided_by,
578
+ datetime(d.ts, 'unixepoch') as updated
579
+ FROM t_decisions d
580
+ JOIN m_context_keys k ON d.key_id = k.id
581
+ LEFT JOIN m_layers l ON d.layer_id = l.id
582
+ LEFT JOIN m_agents a ON d.agent_id = a.id
583
+ WHERE d.status = 1 AND d.ts > (strftime('%s','now') - 3600)
584
+ ORDER BY d.ts DESC
585
+ `);
586
+ // v_layer_summary - Must match bootstrap schema (correct column names)
587
+ await knex.raw(`
588
+ CREATE VIEW v_layer_summary AS
589
+ SELECT
590
+ l.name as layer,
591
+ l.id as layer_id,
592
+ (SELECT COUNT(DISTINCT d2.key_id) FROM t_decisions d2
593
+ WHERE d2.layer_id = l.id AND d2.status = 1 AND d2.project_id = d.project_id) as decisions_count,
594
+ (SELECT COUNT(DISTINCT fc2.id) FROM t_file_changes fc2
595
+ WHERE fc2.layer_id = l.id AND fc2.ts > (strftime('%s','now') - 3600) AND fc2.project_id = d.project_id) as file_changes_count,
596
+ (SELECT COUNT(DISTINCT c2.id) FROM t_constraints c2
597
+ WHERE c2.layer_id = l.id AND c2.active = 1 AND c2.project_id = d.project_id) as constraints_count,
598
+ d.project_id
599
+ FROM m_layers l
600
+ CROSS JOIN (SELECT DISTINCT project_id FROM t_decisions) d
601
+ GROUP BY l.id, l.name, d.project_id
602
+ `);
603
+ // v_recent_file_changes
604
+ await knex.raw(`
605
+ CREATE VIEW v_recent_file_changes AS
606
+ SELECT f.path as file_path,
607
+ fc.project_id,
608
+ l.name as layer,
609
+ a.name as changed_by,
610
+ datetime(fc.ts, 'unixepoch') as changed_at
611
+ FROM t_file_changes fc
612
+ JOIN m_files f ON fc.file_id = f.id
613
+ LEFT JOIN m_layers l ON fc.layer_id = l.id
614
+ LEFT JOIN m_agents a ON fc.agent_id = a.id
615
+ ORDER BY fc.ts DESC
616
+ LIMIT 50
617
+ `);
618
+ // v_tagged_constraints
619
+ await knex.raw(`
620
+ CREATE VIEW v_tagged_constraints AS
621
+ SELECT c.id,
622
+ c.constraint_text,
623
+ c.project_id,
624
+ cat.name as category,
625
+ c.priority,
626
+ a.name as author,
627
+ datetime(c.ts, 'unixepoch') as created
628
+ FROM t_constraints c
629
+ LEFT JOIN m_constraint_categories cat ON c.category_id = cat.id
630
+ LEFT JOIN m_agents a ON c.agent_id = a.id
631
+ WHERE c.active = 1
632
+ ORDER BY c.priority DESC, c.ts DESC
633
+ `);
634
+ // v_task_board - Must match bootstrap schema (include all columns)
635
+ await knex.raw(`
636
+ CREATE VIEW v_task_board AS
637
+ SELECT
638
+ t.id,
639
+ t.title,
640
+ t.project_id,
641
+ s.name as status,
642
+ t.priority,
643
+ a.name as assigned_to,
644
+ l.name as layer,
645
+ t.created_ts,
646
+ t.updated_ts,
647
+ t.completed_ts,
648
+ (SELECT GROUP_CONCAT(tg2.name, ', ')
649
+ FROM t_task_tags tt2
650
+ JOIN m_tags tg2 ON tt2.tag_id = tg2.id
651
+ WHERE tt2.task_id = t.id AND tt2.project_id = t.project_id) as tags
652
+ FROM t_tasks t
653
+ LEFT JOIN m_task_statuses s ON t.status_id = s.id
654
+ LEFT JOIN m_agents a ON t.assigned_agent_id = a.id
655
+ LEFT JOIN m_layers l ON t.layer_id = l.id
656
+ `);
657
+ console.log('✓ Recreated all 6 views with project_id support');
658
+ // ============================================================================
659
+ // STEP 8: Re-enable Foreign Key Constraints
660
+ // ============================================================================
661
+ await knex.raw('PRAGMA foreign_keys = ON');
662
+ console.log('✓ Re-enabled foreign key constraints');
663
+ console.log('✅ Multi-project support migration v3.7.0 completed successfully');
664
+ }
665
+ export async function down(knex) {
666
+ console.log('🔄 Rolling back multi-project support migration...');
667
+ // Drop views
668
+ await knex.raw('DROP VIEW IF EXISTS v_tagged_decisions');
669
+ await knex.raw('DROP VIEW IF EXISTS v_active_context');
670
+ await knex.raw('DROP VIEW IF EXISTS v_layer_summary');
671
+ await knex.raw('DROP VIEW IF EXISTS v_recent_file_changes');
672
+ await knex.raw('DROP VIEW IF EXISTS v_tagged_constraints');
673
+ await knex.raw('DROP VIEW IF EXISTS v_task_board');
674
+ // Drop indexes
675
+ const indexes = [
676
+ 'idx_decisions_project_key',
677
+ 'idx_decisions_project_ts',
678
+ 'idx_decisions_numeric_project_key',
679
+ 'idx_decision_tags_project_key',
680
+ 'idx_tasks_project_status',
681
+ 'idx_tasks_project_created',
682
+ 'idx_config_project_key',
683
+ ];
684
+ for (const indexName of indexes) {
685
+ await knex.raw(`DROP INDEX IF EXISTS ${indexName}`);
686
+ }
687
+ // Restore m_config to original structure
688
+ const configHasProjectId = await knex.schema.hasColumn('m_config', 'project_id');
689
+ if (configHasProjectId) {
690
+ await knex.schema.dropTableIfExists('m_config_old');
691
+ await knex.schema.createTable('m_config_old', (table) => {
692
+ table.string('key', 64).primary();
693
+ table.text('value').notNullable();
694
+ });
695
+ const configData = await knex('m_config').where({ project_id: null }).select('key', 'value');
696
+ if (configData.length > 0) {
697
+ await knex('m_config_old').insert(configData);
698
+ }
699
+ await knex.schema.dropTable('m_config');
700
+ await knex.schema.renameTable('m_config_old', 'm_config');
701
+ }
702
+ // Restore t_decisions tables to single-column PRIMARY KEY
703
+ const decisionsData = await knex('t_decisions').select('*');
704
+ await knex.schema.dropTableIfExists('t_decisions');
705
+ await knex.schema.createTable('t_decisions', (table) => {
706
+ table.integer('key_id').unsigned().primary();
707
+ table.text('value').notNullable();
708
+ table.integer('agent_id').unsigned();
709
+ table.integer('layer_id').unsigned();
710
+ table.string('version', 20).defaultTo('1.0.0');
711
+ table.integer('status').defaultTo(1);
712
+ table.integer('ts').notNullable();
713
+ table.foreign('key_id').references('m_context_keys.id');
714
+ table.foreign('agent_id').references('m_agents.id');
715
+ table.foreign('layer_id').references('m_layers.id');
716
+ });
717
+ // Note: Data may be lost if there were multiple projects
718
+ if (decisionsData.length > 0) {
719
+ await knex('t_decisions').insert(decisionsData.map((row) => {
720
+ const { project_id, ...rest } = row;
721
+ return rest;
722
+ }));
723
+ }
724
+ // Same for t_decisions_numeric
725
+ const decisionsNumericData = await knex('t_decisions_numeric').select('*');
726
+ await knex.schema.dropTableIfExists('t_decisions_numeric');
727
+ await knex.schema.createTable('t_decisions_numeric', (table) => {
728
+ table.integer('key_id').unsigned().primary();
729
+ table.double('value').notNullable();
730
+ table.integer('agent_id').unsigned();
731
+ table.integer('layer_id').unsigned();
732
+ table.string('version', 20).defaultTo('1.0.0');
733
+ table.integer('status').defaultTo(1);
734
+ table.integer('ts').notNullable();
735
+ table.foreign('key_id').references('m_context_keys.id');
736
+ table.foreign('agent_id').references('m_agents.id');
737
+ table.foreign('layer_id').references('m_layers.id');
738
+ });
739
+ if (decisionsNumericData.length > 0) {
740
+ await knex('t_decisions_numeric').insert(decisionsNumericData.map((row) => {
741
+ const { project_id, ...rest } = row;
742
+ return rest;
743
+ }));
744
+ }
745
+ // Remove project_id from all transaction tables
746
+ const transactionTables = [
747
+ 't_task_dependencies',
748
+ 't_task_decision_links',
749
+ 't_task_file_links',
750
+ 't_task_tags',
751
+ 't_task_details',
752
+ 't_tasks',
753
+ 't_constraints',
754
+ 't_file_changes',
755
+ 't_decision_context',
756
+ 't_decision_scopes',
757
+ 't_decision_tags',
758
+ 't_decision_history',
759
+ ];
760
+ for (const tableName of transactionTables) {
761
+ const hasTable = await knex.schema.hasTable(tableName);
762
+ if (hasTable) {
763
+ const hasColumn = await knex.schema.hasColumn(tableName, 'project_id');
764
+ if (hasColumn) {
765
+ await knex.schema.alterTable(tableName, (table) => {
766
+ table.dropColumn('project_id');
767
+ });
768
+ console.log(`✓ Removed project_id from ${tableName}`);
769
+ }
770
+ }
771
+ }
772
+ // Drop migration marker
773
+ await knex.schema.dropTableIfExists('_multi_project_pk_fixed');
774
+ // Drop m_projects table
775
+ await knex.schema.dropTableIfExists('m_projects');
776
+ // Recreate original views (without project_id)
777
+ await knex.raw(`
778
+ CREATE VIEW v_tagged_decisions AS
779
+ SELECT
780
+ k.key,
781
+ d.value,
782
+ l.name as layer,
783
+ a.name as decided_by,
784
+ datetime(d.ts, 'unixepoch') as updated,
785
+ GROUP_CONCAT(t.name, ', ') as tags
786
+ FROM t_decisions d
787
+ JOIN m_context_keys k ON d.key_id = k.id
788
+ LEFT JOIN m_layers l ON d.layer_id = l.id
789
+ LEFT JOIN m_agents a ON d.agent_id = a.id
790
+ LEFT JOIN t_decision_tags dt ON d.key_id = dt.decision_key_id
791
+ LEFT JOIN m_tags t ON dt.tag_id = t.id
792
+ WHERE d.status = 1
793
+ GROUP BY d.key_id
794
+ ORDER BY d.ts DESC
795
+ `);
796
+ await knex.raw(`
797
+ CREATE VIEW v_active_context AS
798
+ SELECT
799
+ k.key,
800
+ d.value,
801
+ l.name as layer,
802
+ a.name as decided_by,
803
+ datetime(d.ts, 'unixepoch') as updated
804
+ FROM t_decisions d
805
+ JOIN m_context_keys k ON d.key_id = k.id
806
+ LEFT JOIN m_layers l ON d.layer_id = l.id
807
+ LEFT JOIN m_agents a ON d.agent_id = a.id
808
+ WHERE d.status = 1 AND d.ts > (strftime('%s','now') - 3600)
809
+ ORDER BY d.ts DESC
810
+ `);
811
+ await knex.raw(`
812
+ CREATE VIEW v_layer_summary AS
813
+ SELECT l.name as layer,
814
+ COUNT(*) as decision_count
815
+ FROM t_decisions d
816
+ JOIN m_layers l ON d.layer_id = l.id
817
+ WHERE d.status = 1
818
+ GROUP BY l.id, l.name
819
+ ORDER BY decision_count DESC
820
+ `);
821
+ await knex.raw(`
822
+ CREATE VIEW v_recent_file_changes AS
823
+ SELECT f.path as file_path,
824
+ l.name as layer,
825
+ a.name as changed_by,
826
+ datetime(fc.ts, 'unixepoch') as changed_at
827
+ FROM t_file_changes fc
828
+ JOIN m_files f ON fc.file_id = f.id
829
+ LEFT JOIN m_layers l ON fc.layer_id = l.id
830
+ LEFT JOIN m_agents a ON fc.agent_id = a.id
831
+ ORDER BY fc.ts DESC
832
+ LIMIT 50
833
+ `);
834
+ await knex.raw(`
835
+ CREATE VIEW v_tagged_constraints AS
836
+ SELECT c.id,
837
+ c.constraint_text,
838
+ cat.name as category,
839
+ c.priority,
840
+ a.name as author,
841
+ datetime(c.ts, 'unixepoch') as created
842
+ FROM t_constraints c
843
+ LEFT JOIN m_constraint_categories cat ON c.category_id = cat.id
844
+ LEFT JOIN m_agents a ON c.agent_id = a.id
845
+ WHERE c.active = 1
846
+ ORDER BY c.priority DESC, c.ts DESC
847
+ `);
848
+ await knex.raw(`
849
+ CREATE VIEW v_task_board AS
850
+ SELECT t.id,
851
+ t.title,
852
+ ts.name as status,
853
+ t.priority,
854
+ a.name as assigned_to,
855
+ datetime(t.created_ts, 'unixepoch') as created,
856
+ datetime(t.updated_ts, 'unixepoch') as updated
857
+ FROM t_tasks t
858
+ JOIN m_task_statuses ts ON t.status_id = ts.id
859
+ LEFT JOIN m_agents a ON t.assigned_agent_id = a.id
860
+ ORDER BY t.priority DESC, t.created_ts DESC
861
+ `);
862
+ console.log('✅ Multi-project rollback completed');
863
+ }
864
+ //# sourceMappingURL=20251104000000_add_multi_project_v3_7_0.js.map