@tekmidian/pai 0.66.1 → 0.66.2

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 (158) hide show
  1. package/dist/{aibroker-client-B8c42Lh8.mjs → aibroker-client-CHEEvJZW.mjs} +2 -2
  2. package/dist/{aibroker-client-C5Fw7DNz.mjs → aibroker-client-Dfv7j1Od.mjs} +3 -3
  3. package/dist/{aibroker-client-C5Fw7DNz.mjs.map → aibroker-client-Dfv7j1Od.mjs.map} +1 -1
  4. package/dist/{auto-route-o0BOsXn7.mjs → auto-route-BnizyALK.mjs} +65 -4
  5. package/dist/auto-route-BnizyALK.mjs.map +1 -0
  6. package/dist/auto-route-D6fW1Q8z.mjs +3 -0
  7. package/dist/{chain-CZfKIEq9.mjs → chain-D5876UQX.mjs} +3 -4
  8. package/dist/{chain-CZfKIEq9.mjs.map → chain-D5876UQX.mjs.map} +1 -1
  9. package/dist/cli/index.mjs +18 -23
  10. package/dist/cli/index.mjs.map +1 -1
  11. package/dist/cli/program.mjs +17 -22
  12. package/dist/{config-BbLFD7Uf.mjs → config-C-LiGlop.mjs} +2 -2
  13. package/dist/{config-BbLFD7Uf.mjs.map → config-C-LiGlop.mjs.map} +1 -1
  14. package/dist/{config-YinjgXEJ.mjs → config-DKIA7wgF.mjs} +1 -1
  15. package/dist/{context-handover-cache-pkHzmL3c.mjs → context-handover-cache-9PGvXRIw.mjs} +984 -10
  16. package/dist/context-handover-cache-9PGvXRIw.mjs.map +1 -0
  17. package/dist/daemon/index.mjs +17 -19
  18. package/dist/daemon/index.mjs.map +1 -1
  19. package/dist/daemon-BUgNvMUh.mjs +5029 -0
  20. package/dist/daemon-BUgNvMUh.mjs.map +1 -0
  21. package/dist/daemon-DL0eglMJ.mjs +18 -0
  22. package/dist/daemon-mcp/index.mjs +12 -9
  23. package/dist/daemon-mcp/index.mjs.map +1 -1
  24. package/dist/{embeddings-CcscYWwk.mjs → embeddings-CEBGrzwu.mjs} +1 -1
  25. package/dist/{embeddings-Bx3q0QOY.mjs → embeddings-DOLZnT1X.mjs} +1 -1
  26. package/dist/{embeddings-Bx3q0QOY.mjs.map → embeddings-DOLZnT1X.mjs.map} +1 -1
  27. package/dist/{env-JNEIrQWg.mjs → env-DiolswKQ.mjs} +1 -1
  28. package/dist/{env-JNEIrQWg.mjs.map → env-DiolswKQ.mjs.map} +1 -1
  29. package/dist/factory-DrBg24UT.mjs +8 -0
  30. package/dist/factory-Ypf8r0fK.mjs +2892 -0
  31. package/dist/factory-Ypf8r0fK.mjs.map +1 -0
  32. package/dist/{fallback-CIiq2Raw.mjs → fallback-D51R4shY.mjs} +23 -11
  33. package/dist/fallback-D51R4shY.mjs.map +1 -0
  34. package/dist/{chunker-BH4i-F2b.mjs → helpers-CZsi_49C.mjs} +221 -2
  35. package/dist/helpers-CZsi_49C.mjs.map +1 -0
  36. package/dist/hooks/worker-status-line.mjs.map +2 -2
  37. package/dist/index.mjs +6 -7
  38. package/dist/{ipc-client-D16Xw6Uo.mjs → ipc-client-BLYX51nG.mjs} +2 -2
  39. package/dist/{ipc-client-D16Xw6Uo.mjs.map → ipc-client-BLYX51nG.mjs.map} +1 -1
  40. package/dist/main-resolver-BYgP86C1.mjs +7 -0
  41. package/dist/{main-resolver-DPPwHNtn.mjs → main-resolver-kVkMJZUD.mjs} +6 -6
  42. package/dist/{main-resolver-DPPwHNtn.mjs.map → main-resolver-kVkMJZUD.mjs.map} +1 -1
  43. package/dist/{migrate-BD7D8EEh.mjs → migrate-Bq0esMOa.mjs} +2 -2
  44. package/dist/{migrate-BD7D8EEh.mjs.map → migrate-Bq0esMOa.mjs.map} +1 -1
  45. package/dist/{pai-marker-D1MMswkz.mjs → pai-marker-DXVpFsYz.mjs} +1 -1
  46. package/dist/{pai-marker-D1MMswkz.mjs.map → pai-marker-DXVpFsYz.mjs.map} +1 -1
  47. package/dist/{planner-Dm5Acyo1.mjs → planner-D5yt0EfA.mjs} +10 -7
  48. package/dist/{planner-Dm5Acyo1.mjs.map → planner-D5yt0EfA.mjs.map} +1 -1
  49. package/dist/postgres-BmLr0MUm.mjs +5 -0
  50. package/dist/{postgres-Ceqsa64C.mjs → postgres-D3xc2RB4.mjs} +364 -9
  51. package/dist/postgres-D3xc2RB4.mjs.map +1 -0
  52. package/dist/{program-JJ0TwSHI.mjs → program-DIXRtAFy.mjs} +77 -70
  53. package/dist/program-DIXRtAFy.mjs.map +1 -0
  54. package/dist/{query-feedback-BIaZTTFO.mjs → query-feedback-B7FYE4JR.mjs} +1 -1
  55. package/dist/{query-feedback-BIaZTTFO.mjs.map → query-feedback-B7FYE4JR.mjs.map} +1 -1
  56. package/dist/{reranker-DKv80KO5.mjs → reranker-3lnggwgq.mjs} +1 -1
  57. package/dist/{reranker-DKv80KO5.mjs.map → reranker-3lnggwgq.mjs.map} +1 -1
  58. package/dist/{reranker-CFiEzHQu.mjs → reranker-CZ2mP4cf.mjs} +1 -1
  59. package/dist/router-BK-hFeQ6.mjs +3 -0
  60. package/dist/{router-a5G7q8_3.mjs → router-BaTbc9VX.mjs} +2 -2
  61. package/dist/{router-a5G7q8_3.mjs.map → router-BaTbc9VX.mjs.map} +1 -1
  62. package/dist/{run-BCpbxqA5.mjs → run-CLYTOm0x.mjs} +599 -22
  63. package/dist/run-CLYTOm0x.mjs.map +1 -0
  64. package/dist/{run-env-DRcp7A8K.mjs → run-env-Bnmf6bRI.mjs} +2 -2
  65. package/dist/{run-env-DRcp7A8K.mjs.map → run-env-Bnmf6bRI.mjs.map} +1 -1
  66. package/dist/{runtime-paths-CHTg3ywb.mjs → runtime-paths-D3FKAm69.mjs} +1 -1
  67. package/dist/{runtime-paths-CHTg3ywb.mjs.map → runtime-paths-D3FKAm69.mjs.map} +1 -1
  68. package/dist/{search-BOSphCJ1.mjs → search-CNAGTiJP.mjs} +2 -2
  69. package/dist/{search-BOSphCJ1.mjs.map → search-CNAGTiJP.mjs.map} +1 -1
  70. package/dist/{sources-CijVso3n.mjs → sources-CM2g-CLT.mjs} +2 -2
  71. package/dist/{sources-CijVso3n.mjs.map → sources-CM2g-CLT.mjs.map} +1 -1
  72. package/dist/{stop-words-BdQuaE9K.mjs → stop-words-DtxaTWU_.mjs} +1 -1
  73. package/dist/{stop-words-BdQuaE9K.mjs.map → stop-words-DtxaTWU_.mjs.map} +1 -1
  74. package/dist/{utils-C9HsYDpO.mjs → utils-DddRwMsG.mjs} +2 -2
  75. package/dist/{utils-C9HsYDpO.mjs.map → utils-DddRwMsG.mjs.map} +1 -1
  76. package/dist/{zettelkasten-CtGHQWPU.mjs → zettelkasten-BhZMvmoK.mjs} +149 -7
  77. package/dist/zettelkasten-BhZMvmoK.mjs.map +1 -0
  78. package/dist/zettelkasten-BwV1pluL.mjs +5 -0
  79. package/docs/commands/worker.md +2 -0
  80. package/package.json +1 -1
  81. package/dist/async-C2Bm_Lal.mjs +0 -300
  82. package/dist/async-C2Bm_Lal.mjs.map +0 -1
  83. package/dist/auto-route-o0BOsXn7.mjs.map +0 -1
  84. package/dist/chunker-BH4i-F2b.mjs.map +0 -1
  85. package/dist/clusters-BCtD3fbe.mjs +0 -169
  86. package/dist/clusters-BCtD3fbe.mjs.map +0 -1
  87. package/dist/context-handover-cache-pkHzmL3c.mjs.map +0 -1
  88. package/dist/daemon-DXlaVCkw.mjs +0 -1741
  89. package/dist/daemon-DXlaVCkw.mjs.map +0 -1
  90. package/dist/daemon-DyORmxZu.mjs +0 -19
  91. package/dist/detector-BEPFsINR.mjs +0 -3
  92. package/dist/detector-BRTtAWrM.mjs +0 -65
  93. package/dist/detector-BRTtAWrM.mjs.map +0 -1
  94. package/dist/factory-CNpPQQ-5.mjs +0 -249
  95. package/dist/factory-CNpPQQ-5.mjs.map +0 -1
  96. package/dist/factory-DY7x73mE.mjs +0 -3
  97. package/dist/fallback-CIiq2Raw.mjs.map +0 -1
  98. package/dist/federation-db-BTyoufBh.mjs +0 -139
  99. package/dist/federation-db-BTyoufBh.mjs.map +0 -1
  100. package/dist/federation-db-HfIFc7FG.mjs +0 -3
  101. package/dist/helpers-BRCJg0G3.mjs +0 -223
  102. package/dist/helpers-BRCJg0G3.mjs.map +0 -1
  103. package/dist/indexer-backend-DFF2FrYx.mjs +0 -5
  104. package/dist/indexer-backend-isSLg6yE.mjs +0 -1
  105. package/dist/kg-entity-DCOcsVFD.mjs +0 -29
  106. package/dist/kg-entity-DCOcsVFD.mjs.map +0 -1
  107. package/dist/latent-ideas-wwAXquGe.mjs +0 -191
  108. package/dist/latent-ideas-wwAXquGe.mjs.map +0 -1
  109. package/dist/link-boost-CnI7UVMJ.mjs +0 -34
  110. package/dist/link-boost-CnI7UVMJ.mjs.map +0 -1
  111. package/dist/main-resolver-BKz_OuEh.mjs +0 -7
  112. package/dist/merge-2gqRPFu2.mjs +0 -3
  113. package/dist/merge-DgU9OgZy.mjs +0 -6
  114. package/dist/merge-DgU9OgZy.mjs.map +0 -1
  115. package/dist/module-paths-DdRzbkUI.mjs +0 -44
  116. package/dist/module-paths-DdRzbkUI.mjs.map +0 -1
  117. package/dist/neighborhood-2FsmzoxG.mjs +0 -114
  118. package/dist/neighborhood-2FsmzoxG.mjs.map +0 -1
  119. package/dist/note-context-De63k8na.mjs +0 -106
  120. package/dist/note-context-De63k8na.mjs.map +0 -1
  121. package/dist/postgres-Ceqsa64C.mjs.map +0 -1
  122. package/dist/program-JJ0TwSHI.mjs.map +0 -1
  123. package/dist/query-feedback-B_iigYj-.mjs +0 -3
  124. package/dist/registry-db-C7voqML9.mjs +0 -213
  125. package/dist/registry-db-C7voqML9.mjs.map +0 -1
  126. package/dist/registry-db-JHPhA8vF.mjs +0 -3
  127. package/dist/registry-postgres-nOfyBSIW.mjs +0 -795
  128. package/dist/registry-postgres-nOfyBSIW.mjs.map +0 -1
  129. package/dist/registry-sqlite-DrCW1aRK.mjs +0 -593
  130. package/dist/registry-sqlite-DrCW1aRK.mjs.map +0 -1
  131. package/dist/router-CXUGsv85.mjs +0 -3
  132. package/dist/run-BCpbxqA5.mjs.map +0 -1
  133. package/dist/search-Bf3Kub3F.mjs +0 -3
  134. package/dist/server-DgmAHyFK.mjs +0 -403
  135. package/dist/server-DgmAHyFK.mjs.map +0 -1
  136. package/dist/session-keepalive-DxjTHGcK.mjs +0 -582
  137. package/dist/session-keepalive-DxjTHGcK.mjs.map +0 -1
  138. package/dist/sqlite-CqsTy6xo.mjs +0 -936
  139. package/dist/sqlite-CqsTy6xo.mjs.map +0 -1
  140. package/dist/state-DW8zdweW.mjs +0 -3
  141. package/dist/state-HyjqTihC.mjs +0 -76
  142. package/dist/state-HyjqTihC.mjs.map +0 -1
  143. package/dist/themes-CTaOj3e1.mjs +0 -148
  144. package/dist/themes-CTaOj3e1.mjs.map +0 -1
  145. package/dist/tools-BcOKPBtg.mjs +0 -1489
  146. package/dist/tools-BcOKPBtg.mjs.map +0 -1
  147. package/dist/tools-C-_l0o8Z.mjs +0 -6
  148. package/dist/trace-CtV0RO6n.mjs +0 -137
  149. package/dist/trace-CtV0RO6n.mjs.map +0 -1
  150. package/dist/utils-BrX9FLeK.mjs +0 -3
  151. package/dist/vault-indexer-BG1sEVyM.mjs +0 -537
  152. package/dist/vault-indexer-BG1sEVyM.mjs.map +0 -1
  153. package/dist/wakeup-D8n33hYV.mjs +0 -335
  154. package/dist/wakeup-D8n33hYV.mjs.map +0 -1
  155. package/dist/work-queue-worker-4tLa5gpQ.mjs +0 -567
  156. package/dist/work-queue-worker-4tLa5gpQ.mjs.map +0 -1
  157. package/dist/work-queue-worker-rFZvTO3p.mjs +0 -11
  158. package/dist/zettelkasten-CtGHQWPU.mjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"registry-postgres-nOfyBSIW.mjs","names":[],"sources":["../src/storage/registry-postgres.ts"],"sourcesContent":["/**\n * PostgresRegistryBackend — implements RegistryBackend over the pg pool\n * pattern already used by PostgresBackend (src/storage/postgres/backend.ts).\n *\n * Same SQL shape as SQLiteRegistryBackend, translated to $-placeholders.\n * Behavioural notes where Postgres diverges from SQLite's \"OR IGNORE\":\n * - `INSERT ... ON CONFLICT DO NOTHING` (no target) catches any unique\n * violation on the table, matching SQLite's INSERT OR IGNORE.\n * - SQLite's `UPDATE OR IGNORE` (used once, in reassignLinksTarget/\n * applyProjectMerge's link retarget) has no direct Postgres equivalent —\n * an UPDATE cannot specify ON CONFLICT. Replicated with a NOT EXISTS guard\n * that skips rows whose new (session_id, target_project_id) would collide,\n * leaving them for the caller's existing cleanup DELETE, exactly as the\n * SQLite version does.\n */\n\nimport pg from \"pg\";\nimport type { Pool, PoolClient } from \"pg\";\nimport type { PostgresConfig } from \"./postgres/config.js\";\nimport type {\n RegistryBackend,\n Project,\n ProjectWithSessionStats,\n Session,\n SessionWithProject,\n NewProject,\n NewProjectWithSlugRetry,\n NewSession,\n NewCompactionLogEntry,\n ListProjectsOptions,\n ListProjectsByPathLengthOptions,\n ListProjectsWithStatsOptions,\n ListNamedProjectsOptions,\n ListSessionsOptions,\n ListSessionsForProjectOptions,\n ProjectType,\n ProjectStatus,\n SessionStatus,\n LinkType,\n MergePlan,\n} from \"./registry-interface.js\";\n\nconst { Pool: PgPool } = pg;\n\n/**\n * node-postgres returns BIGINT columns (created_at/updated_at/archived_at/\n * closed_at, and MAX() over a BIGINT column) as strings — JS numbers can't\n * safely hold the full 64-bit range. Every value here is Date.now() ms and\n * well inside Number.isSafeInteger, so a plain Number() conversion keeps the\n * shape identical to better-sqlite3's row objects (same pattern as\n * rowToKgEntity in postgres/backend.ts).\n */\nfunction toNum(v: unknown): number {\n return typeof v === \"string\" ? Number(v) : (v as number);\n}\nfunction toNumOrNull(v: unknown): number | null {\n return v === null || v === undefined ? null : toNum(v);\n}\n\nfunction mapProject(row: Record<string, unknown>): Project {\n return {\n ...row,\n created_at: toNum(row.created_at),\n updated_at: toNum(row.updated_at),\n archived_at: toNumOrNull(row.archived_at),\n } as Project;\n}\n\nfunction mapProjectWithStats(row: Record<string, unknown>): ProjectWithSessionStats {\n return {\n ...mapProject(row),\n session_count: toNum(row.session_count),\n last_active: toNumOrNull(row.last_active),\n } as ProjectWithSessionStats;\n}\n\nfunction mapSession(row: Record<string, unknown>): Session {\n return {\n ...row,\n created_at: toNum(row.created_at),\n closed_at: toNumOrNull(row.closed_at),\n } as Session;\n}\n\nexport class PostgresRegistryBackend implements RegistryBackend {\n readonly backendType = \"postgres\" as const;\n\n private pool: Pool;\n /** False when constructed from a pool owned elsewhere (the shared storage\n * backend pool, wired by src/storage/factory.ts) — close() must not end\n * a pool this instance doesn't own. */\n private readonly ownsPool: boolean;\n\n constructor(configOrPool: PostgresConfig | Pool) {\n if (configOrPool instanceof PgPool) {\n this.pool = configOrPool;\n this.ownsPool = false;\n return;\n }\n\n const config = configOrPool;\n const connStr =\n config.connectionString ??\n `postgresql://${config.user ?? \"pai\"}:${config.password ?? \"pai\"}@${config.host ?? \"localhost\"}:${config.port ?? 5432}/${config.database ?? \"pai\"}`;\n\n this.pool = new PgPool({\n connectionString: connStr,\n max: config.maxConnections ?? 5,\n connectionTimeoutMillis: config.connectionTimeoutMs ?? 5000,\n idleTimeoutMillis: 30_000,\n allowExitOnIdle: true,\n });\n this.ownsPool = true;\n\n this.pool.on(\"error\", (err) => {\n process.stderr.write(`[pai-postgres] Registry pool error: ${err.message}\\n`);\n });\n }\n\n async close(): Promise<void> {\n if (this.ownsPool) await this.pool.end();\n }\n\n async resetRegistry(): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query(\"BEGIN\");\n await client.query(\"DELETE FROM links\");\n await client.query(\"DELETE FROM compaction_log\");\n await client.query(\"DELETE FROM session_tags\");\n await client.query(\"DELETE FROM project_tags\");\n await client.query(\"DELETE FROM aliases\");\n await client.query(\"DELETE FROM sessions\");\n await client.query(\"DELETE FROM tags\");\n await client.query(\"DELETE FROM projects\");\n await client.query(\"COMMIT\");\n } catch (e) {\n await client.query(\"ROLLBACK\");\n throw e;\n } finally {\n client.release();\n }\n }\n\n // -------------------------------------------------------------------------\n // Projects — reads\n // -------------------------------------------------------------------------\n\n async getProjectById(id: number): Promise<Project | null> {\n const r = await this.pool.query(\"SELECT * FROM projects WHERE id = $1\", [id]);\n return r.rows[0] ? mapProject(r.rows[0]) : null;\n }\n\n async getProjectBySlug(\n slug: string,\n opts: { caseInsensitive?: boolean; excludeId?: number } = {}\n ): Promise<Project | null> {\n let sql = opts.caseInsensitive\n ? \"SELECT * FROM projects WHERE lower(slug) = lower($1)\"\n : \"SELECT * FROM projects WHERE slug = $1\";\n const params: unknown[] = [slug];\n if (opts.excludeId !== undefined) {\n params.push(opts.excludeId);\n sql += ` AND id != $${params.length}`;\n }\n const r = await this.pool.query(sql, params);\n return r.rows[0] ? mapProject(r.rows[0]) : null;\n }\n\n async getProjectByAlias(\n alias: string,\n opts: { caseInsensitive?: boolean } = {}\n ): Promise<Project | null> {\n const sql = opts.caseInsensitive\n ? \"SELECT p.* FROM projects p JOIN aliases a ON a.project_id = p.id WHERE lower(a.alias) = lower($1)\"\n : \"SELECT p.* FROM projects p JOIN aliases a ON a.project_id = p.id WHERE a.alias = $1\";\n const r = await this.pool.query(sql, [alias]);\n return r.rows[0] ? mapProject(r.rows[0]) : null;\n }\n\n async getProjectByRootPath(rootPath: string, opts: { excludeId?: number } = {}): Promise<Project | null> {\n let sql = \"SELECT * FROM projects WHERE root_path = $1\";\n const params: unknown[] = [rootPath];\n if (opts.excludeId !== undefined) {\n params.push(opts.excludeId);\n sql += ` AND id != $${params.length}`;\n }\n const r = await this.pool.query(sql, params);\n return r.rows[0] ? mapProject(r.rows[0]) : null;\n }\n\n async getProjectByEncodedDir(encodedDir: string, opts: { excludeId?: number } = {}): Promise<Project | null> {\n let sql = \"SELECT * FROM projects WHERE encoded_dir = $1\";\n const params: unknown[] = [encodedDir];\n if (opts.excludeId !== undefined) {\n params.push(opts.excludeId);\n sql += ` AND id != $${params.length}`;\n }\n const r = await this.pool.query(sql, params);\n return r.rows[0] ? mapProject(r.rows[0]) : null;\n }\n\n async findProjectByCwdPrefix(cwd: string): Promise<Project | null> {\n const r = await this.pool.query(\n `SELECT * FROM projects WHERE status = 'active' AND $1 LIKE root_path || '%'\n ORDER BY length(root_path) DESC LIMIT 1`,\n [cwd]\n );\n return r.rows[0] ? mapProject(r.rows[0]) : null;\n }\n\n async listProjects(opts: ListProjectsOptions = {}): Promise<Project[]> {\n const where: string[] = [];\n const params: unknown[] = [];\n\n if (opts.status) {\n params.push(opts.status);\n where.push(`p.status = $${params.length}`);\n }\n if (opts.excludeArchived) {\n where.push(\"p.status != 'archived'\");\n }\n if (opts.tagName) {\n params.push(opts.tagName);\n where.push(\n `p.id IN (SELECT pt.project_id FROM project_tags pt JOIN tags t ON pt.tag_id = t.id WHERE t.name = $${params.length})`\n );\n }\n\n let sql = \"SELECT p.* FROM projects p\";\n if (where.length) sql += \" WHERE \" + where.join(\" AND \");\n\n switch (opts.orderBy) {\n case \"updated_desc\":\n sql += \" ORDER BY p.updated_at DESC\";\n break;\n case \"slug\":\n sql += \" ORDER BY p.slug ASC\";\n break;\n case \"id\":\n sql += \" ORDER BY p.id\";\n break;\n case \"status_updated\":\n default:\n sql += \" ORDER BY p.status ASC, p.updated_at DESC\";\n break;\n }\n if (opts.limit !== undefined) {\n params.push(opts.limit);\n sql += ` LIMIT $${params.length}`;\n }\n\n const r = await this.pool.query(sql, params);\n return r.rows.map(mapProject);\n }\n\n async listProjectsByPathLengthDesc(opts: ListProjectsByPathLengthOptions = {}): Promise<Project[]> {\n const where = opts.excludeArchived ? \"WHERE status != 'archived'\" : \"\";\n const r = await this.pool.query(`SELECT * FROM projects ${where} ORDER BY LENGTH(root_path) DESC`);\n return r.rows.map(mapProject);\n }\n\n async listProjectsWithSessionStats(\n opts: ListProjectsWithStatsOptions = {}\n ): Promise<ProjectWithSessionStats[]> {\n const where: string[] = [];\n const params: unknown[] = [];\n\n if (opts.status) {\n params.push(opts.status);\n where.push(`p.status = $${params.length}`);\n }\n if (opts.tagId !== undefined) {\n params.push(opts.tagId);\n where.push(`p.id IN (SELECT project_id FROM project_tags WHERE tag_id = $${params.length})`);\n }\n\n let sql = `\n SELECT p.*,\n (SELECT COUNT(*) FROM sessions s WHERE s.project_id = p.id)::int AS session_count,\n (SELECT MAX(s.created_at) FROM sessions s WHERE s.project_id = p.id) AS last_active\n FROM projects p\n `;\n if (where.length) sql += \" WHERE \" + where.join(\" AND \");\n sql +=\n opts.orderBy === \"updated_desc\"\n ? \" ORDER BY p.updated_at DESC\"\n : \" ORDER BY p.status ASC, p.updated_at DESC\";\n\n const r = await this.pool.query(sql, params);\n return r.rows.map(mapProjectWithStats);\n }\n\n async listNamedProjects(\n opts: ListNamedProjectsOptions = {}\n ): Promise<Array<ProjectWithSessionStats & { name: string | null }>> {\n const join = opts.includeUnnamed ? \"LEFT JOIN\" : \"JOIN\";\n const sql = `\n SELECT p.*, a.alias AS name,\n (SELECT COUNT(*) FROM sessions s WHERE s.project_id = p.id)::int AS session_count,\n (SELECT MAX(s.created_at) FROM sessions s WHERE s.project_id = p.id) AS last_active\n FROM projects p\n ${join} aliases a ON a.project_id = p.id\n WHERE p.status = 'active'\n ORDER BY p.updated_at DESC\n `;\n const r = await this.pool.query(sql);\n return r.rows.map((row) => ({ ...mapProjectWithStats(row), name: row.name as string | null }));\n }\n\n async searchProjects(query: string, limit = 20): Promise<Project[]> {\n const q = `%${query}%`;\n const r = await this.pool.query(\n `SELECT * FROM projects WHERE slug LIKE $1 OR display_name LIKE $1 OR root_path LIKE $1\n ORDER BY updated_at DESC LIMIT $2`,\n [q, limit]\n );\n return r.rows.map(mapProject);\n }\n\n async countProjects(opts: { status?: ProjectStatus } = {}): Promise<number> {\n const r = opts.status\n ? await this.pool.query<{ n: string }>(\"SELECT COUNT(*)::text AS n FROM projects WHERE status = $1\", [\n opts.status,\n ])\n : await this.pool.query<{ n: string }>(\"SELECT COUNT(*)::text AS n FROM projects\");\n return parseInt(r.rows[0].n, 10);\n }\n\n async getMostRecentProjectUpdatedAt(): Promise<number | null> {\n const r = await this.pool.query<{ updated_at: string }>(\n \"SELECT updated_at FROM projects ORDER BY updated_at DESC LIMIT 1\"\n );\n return r.rows[0] ? Number(r.rows[0].updated_at) : null;\n }\n\n async countChildProjects(parentId: number): Promise<number> {\n const r = await this.pool.query<{ n: string }>(\n \"SELECT COUNT(*)::text AS n FROM projects WHERE parent_id = $1\",\n [parentId]\n );\n return parseInt(r.rows[0].n, 10);\n }\n\n async findSiblingProjectsBySlugPattern(\n excludeProjectId: number,\n slug: string,\n limit = 3\n ): Promise<Array<{ slug: string; root_path: string; session_count: number }>> {\n const r = await this.pool.query(\n `SELECT p.slug, p.root_path, COUNT(s.id)::int AS session_count\n FROM projects p LEFT JOIN sessions s ON s.project_id = p.id\n WHERE p.id != $1 AND (p.slug = $2 OR p.slug LIKE $2 || '-%' OR $2 LIKE p.slug || '-%')\n GROUP BY p.id HAVING COUNT(s.id) > 0 ORDER BY session_count DESC LIMIT $3`,\n [excludeProjectId, slug, limit]\n );\n return r.rows as Array<{ slug: string; root_path: string; session_count: number }>;\n }\n\n // -------------------------------------------------------------------------\n // Projects — writes\n // -------------------------------------------------------------------------\n\n async createProject(input: NewProject): Promise<Project> {\n const type: ProjectType = input.type ?? \"local\";\n const status: ProjectStatus = input.status ?? \"active\";\n const r = await this.pool.query(\n `INSERT INTO projects (slug, display_name, root_path, encoded_dir, type, status, created_at, updated_at)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *`,\n [input.slug, input.displayName, input.rootPath, input.encodedDir, type, status, input.createdAt, input.updatedAt]\n );\n return mapProject(r.rows[0]);\n }\n\n async createProjectWithSlugRetry(\n input: NewProjectWithSlugRetry\n ): Promise<{ id: number; slug: string; created: boolean }> {\n let slug = input.baseSlug;\n let attempt = 0;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const r = await this.pool.query<{ id: number }>(\n `INSERT INTO projects (slug, display_name, root_path, encoded_dir, type, status, created_at, updated_at)\n VALUES ($1, $2, $3, $4, 'local', 'active', $5, $6)\n ON CONFLICT DO NOTHING RETURNING id`,\n [slug, input.displayName, input.rootPath, input.encodedDir, input.createdAt, input.updatedAt]\n );\n if (r.rows.length > 0) {\n return { id: r.rows[0].id, slug, created: true };\n }\n\n const existing = await this.pool.query<{ id: number }>(\n \"SELECT id FROM projects WHERE root_path = $1\",\n [input.rootPath]\n );\n if (existing.rows[0]) {\n return { id: existing.rows[0].id, slug, created: false };\n }\n\n attempt++;\n slug = `${input.baseSlug}-${attempt}`;\n }\n }\n\n async updateProjectPath(\n id: number,\n patch: { rootPath?: string; encodedDir?: string },\n updatedAt?: number\n ): Promise<void> {\n const sets: string[] = [];\n const params: unknown[] = [];\n if (patch.rootPath !== undefined) {\n params.push(patch.rootPath);\n sets.push(`root_path = $${params.length}`);\n }\n if (patch.encodedDir !== undefined) {\n params.push(patch.encodedDir);\n sets.push(`encoded_dir = $${params.length}`);\n }\n if (updatedAt !== undefined) {\n params.push(updatedAt);\n sets.push(`updated_at = $${params.length}`);\n }\n if (!sets.length) return;\n params.push(id);\n await this.pool.query(`UPDATE projects SET ${sets.join(\", \")} WHERE id = $${params.length}`, params);\n }\n\n async updateProjectStatus(\n id: number,\n status: ProjectStatus,\n opts: { archivedAt?: number | null; updatedAt?: number; requireCurrentStatus?: ProjectStatus } = {}\n ): Promise<void> {\n const sets: string[] = [];\n const params: unknown[] = [status];\n sets.push(\"status = $1\");\n if (opts.archivedAt !== undefined) {\n params.push(opts.archivedAt);\n sets.push(`archived_at = $${params.length}`);\n }\n if (opts.updatedAt !== undefined) {\n params.push(opts.updatedAt);\n sets.push(`updated_at = $${params.length}`);\n }\n params.push(id);\n let sql = `UPDATE projects SET ${sets.join(\", \")} WHERE id = $${params.length}`;\n if (opts.requireCurrentStatus) {\n params.push(opts.requireCurrentStatus);\n sql += ` AND status = $${params.length}`;\n }\n await this.pool.query(sql, params);\n }\n\n async updateProjectDisplayName(id: number, displayName: string, updatedAt?: number): Promise<void> {\n if (updatedAt !== undefined) {\n await this.pool.query(\"UPDATE projects SET display_name = $1, updated_at = $2 WHERE id = $3\", [\n displayName,\n updatedAt,\n id,\n ]);\n } else {\n await this.pool.query(\"UPDATE projects SET display_name = $1 WHERE id = $2\", [displayName, id]);\n }\n }\n\n async updateProjectType(id: number, type: ProjectType, updatedAt?: number): Promise<void> {\n if (updatedAt !== undefined) {\n await this.pool.query(\"UPDATE projects SET type = $1, updated_at = $2 WHERE id = $3\", [\n type,\n updatedAt,\n id,\n ]);\n } else {\n await this.pool.query(\"UPDATE projects SET type = $1 WHERE id = $2\", [type, id]);\n }\n }\n\n async updateProjectSessionConfig(id: number, config: string | null, updatedAt?: number): Promise<void> {\n if (updatedAt !== undefined) {\n await this.pool.query(\"UPDATE projects SET session_config = $1, updated_at = $2 WHERE id = $3\", [\n config,\n updatedAt,\n id,\n ]);\n } else {\n await this.pool.query(\"UPDATE projects SET session_config = $1 WHERE id = $2\", [config, id]);\n }\n }\n\n async updateProjectClaudeNotesDir(id: number, dir: string | null, updatedAt?: number): Promise<void> {\n if (updatedAt !== undefined) {\n await this.pool.query(\"UPDATE projects SET claude_notes_dir = $1, updated_at = $2 WHERE id = $3\", [\n dir,\n updatedAt,\n id,\n ]);\n } else {\n await this.pool.query(\"UPDATE projects SET claude_notes_dir = $1 WHERE id = $2\", [dir, id]);\n }\n }\n\n async updateProjectObsidianLink(id: number, link: string | null, updatedAt?: number): Promise<void> {\n if (updatedAt !== undefined) {\n await this.pool.query(\"UPDATE projects SET obsidian_link = $1, updated_at = $2 WHERE id = $3\", [\n link,\n updatedAt,\n id,\n ]);\n } else {\n await this.pool.query(\"UPDATE projects SET obsidian_link = $1 WHERE id = $2\", [link, id]);\n }\n }\n\n async updateProjectSlug(id: number, slug: string, updatedAt: number): Promise<void> {\n await this.pool.query(\"UPDATE projects SET slug = $1, updated_at = $2 WHERE id = $3\", [slug, updatedAt, id]);\n }\n\n async reassignProjectParent(fromParentId: number, toParentId: number): Promise<number> {\n const r = await this.pool.query(\"UPDATE projects SET parent_id = $1 WHERE parent_id = $2\", [\n toParentId,\n fromParentId,\n ]);\n return r.rowCount ?? 0;\n }\n\n async deleteProject(id: number): Promise<void> {\n await this.pool.query(\"DELETE FROM projects WHERE id = $1\", [id]);\n }\n\n async deleteProjectCascade(id: number): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query(\"BEGIN\");\n await client.query(\"DELETE FROM links WHERE target_project_id = $1\", [id]);\n await client.query(\n \"DELETE FROM links WHERE session_id IN (SELECT id FROM sessions WHERE project_id = $1)\",\n [id]\n );\n await client.query(\"DELETE FROM compaction_log WHERE project_id = $1\", [id]);\n await client.query(\"DELETE FROM project_tags WHERE project_id = $1\", [id]);\n await client.query(\"DELETE FROM aliases WHERE project_id = $1\", [id]);\n await client.query(\"DELETE FROM sessions WHERE project_id = $1\", [id]);\n await client.query(\"DELETE FROM projects WHERE id = $1\", [id]);\n await client.query(\"COMMIT\");\n } catch (e) {\n await client.query(\"ROLLBACK\");\n throw e;\n } finally {\n client.release();\n }\n }\n\n // -------------------------------------------------------------------------\n // Tags\n // -------------------------------------------------------------------------\n\n async listTagsForProject(projectId: number): Promise<string[]> {\n const r = await this.pool.query<{ name: string }>(\n \"SELECT t.name FROM tags t JOIN project_tags pt ON pt.tag_id = t.id WHERE pt.project_id = $1 ORDER BY t.name\",\n [projectId]\n );\n return r.rows.map((row) => row.name);\n }\n\n async listAllTags(): Promise<Array<{ id: number; name: string }>> {\n const r = await this.pool.query<{ id: number; name: string }>(\"SELECT id, name FROM tags ORDER BY name\");\n return r.rows;\n }\n\n async upsertTag(name: string): Promise<number> {\n await this.pool.query(\"INSERT INTO tags (name) VALUES ($1) ON CONFLICT (name) DO NOTHING\", [name]);\n const r = await this.pool.query<{ id: number }>(\"SELECT id FROM tags WHERE name = $1\", [name]);\n return r.rows[0].id;\n }\n\n async addProjectTag(projectId: number, tagId: number): Promise<void> {\n await this.pool.query(\"INSERT INTO project_tags (project_id, tag_id) VALUES ($1, $2)\", [projectId, tagId]);\n }\n\n async projectHasTag(projectId: number, tagId: number): Promise<boolean> {\n const r = await this.pool.query(\"SELECT 1 FROM project_tags WHERE project_id = $1 AND tag_id = $2\", [\n projectId,\n tagId,\n ]);\n return (r.rowCount ?? 0) > 0;\n }\n\n async deleteProjectTag(projectId: number, tagId: number): Promise<void> {\n await this.pool.query(\"DELETE FROM project_tags WHERE project_id = $1 AND tag_id = $2\", [projectId, tagId]);\n }\n\n async reassignProjectTag(fromProjectId: number, toProjectId: number, tagId: number): Promise<void> {\n await this.pool.query(\"UPDATE project_tags SET project_id = $1 WHERE project_id = $2 AND tag_id = $3\", [\n toProjectId,\n fromProjectId,\n tagId,\n ]);\n }\n\n async listProjectTagIds(projectId: number): Promise<number[]> {\n const r = await this.pool.query<{ tag_id: number }>(\"SELECT tag_id FROM project_tags WHERE project_id = $1\", [\n projectId,\n ]);\n return r.rows.map((row) => row.tag_id);\n }\n\n async countProjectTagsForProject(projectId: number): Promise<number> {\n const r = await this.pool.query<{ n: string }>(\n \"SELECT COUNT(*)::text AS n FROM project_tags WHERE project_id = $1\",\n [projectId]\n );\n return parseInt(r.rows[0].n, 10);\n }\n\n async copyProjectTags(fromProjectId: number, toProjectId: number): Promise<void> {\n await this.pool.query(\n `INSERT INTO project_tags (project_id, tag_id) SELECT $1, tag_id FROM project_tags WHERE project_id = $2\n ON CONFLICT (project_id, tag_id) DO NOTHING`,\n [toProjectId, fromProjectId]\n );\n }\n\n async deleteProjectTagsForProject(projectId: number): Promise<void> {\n await this.pool.query(\"DELETE FROM project_tags WHERE project_id = $1\", [projectId]);\n }\n\n // -------------------------------------------------------------------------\n // Aliases\n // -------------------------------------------------------------------------\n\n async resolveAlias(alias: string): Promise<number | null> {\n const r = await this.pool.query<{ project_id: number }>(\n \"SELECT project_id FROM aliases WHERE alias = $1\",\n [alias]\n );\n return r.rows[0]?.project_id ?? null;\n }\n\n async listAliasesForProject(projectId: number): Promise<string[]> {\n const r = await this.pool.query<{ alias: string }>(\n \"SELECT alias FROM aliases WHERE project_id = $1 ORDER BY alias\",\n [projectId]\n );\n return r.rows.map((row) => row.alias);\n }\n\n async addAlias(alias: string, projectId: number): Promise<void> {\n await this.pool.query(\"INSERT INTO aliases (alias, project_id) VALUES ($1, $2)\", [alias, projectId]);\n }\n\n async removeAlias(alias: string): Promise<void> {\n await this.pool.query(\"DELETE FROM aliases WHERE alias = $1\", [alias]);\n }\n\n async countAliasesForProject(projectId: number): Promise<number> {\n const r = await this.pool.query<{ n: string }>(\n \"SELECT COUNT(*)::text AS n FROM aliases WHERE project_id = $1\",\n [projectId]\n );\n return parseInt(r.rows[0].n, 10);\n }\n\n async moveProjectAliases(fromProjectId: number, toProjectId: number): Promise<void> {\n await this.pool.query(\"UPDATE aliases SET project_id = $1 WHERE project_id = $2\", [\n toProjectId,\n fromProjectId,\n ]);\n }\n\n async reassignAlias(alias: string, toProjectId: number): Promise<void> {\n await this.pool.query(\"UPDATE aliases SET project_id = $1 WHERE alias = $2\", [toProjectId, alias]);\n }\n\n async listAliasMap(): Promise<Array<{ alias: string; slug: string; root_path: string }>> {\n const r = await this.pool.query(\n `SELECT a.alias AS alias, p.slug AS slug, p.root_path AS root_path\n FROM aliases a JOIN projects p ON p.id = a.project_id WHERE p.status != 'archived'`\n );\n return r.rows as Array<{ alias: string; slug: string; root_path: string }>;\n }\n\n // -------------------------------------------------------------------------\n // Sessions\n // -------------------------------------------------------------------------\n\n async getSessionById(id: number): Promise<Session | null> {\n const r = await this.pool.query(\"SELECT * FROM sessions WHERE id = $1\", [id]);\n return r.rows[0] ? mapSession(r.rows[0]) : null;\n }\n\n async getSessionByNumber(projectId: number, number: number): Promise<Session | null> {\n const r = await this.pool.query(\"SELECT * FROM sessions WHERE project_id = $1 AND number = $2\", [\n projectId,\n number,\n ]);\n return r.rows[0] ? mapSession(r.rows[0]) : null;\n }\n\n async getLatestSessionForProject(projectId: number): Promise<Session | null> {\n const r = await this.pool.query(\n \"SELECT * FROM sessions WHERE project_id = $1 ORDER BY number DESC LIMIT 1\",\n [projectId]\n );\n return r.rows[0] ? mapSession(r.rows[0]) : null;\n }\n\n async getMaxSessionNumber(projectId: number): Promise<number> {\n const r = await this.pool.query<{ n: number }>(\n \"SELECT COALESCE(MAX(number), 0) AS n FROM sessions WHERE project_id = $1\",\n [projectId]\n );\n return r.rows[0].n;\n }\n\n async listSessionsForProject(\n projectId: number,\n opts: ListSessionsForProjectOptions = {}\n ): Promise<Session[]> {\n const orderBy = opts.orderBy === \"created_desc\" ? \"created_at DESC\" : \"number ASC\";\n let sql = `SELECT * FROM sessions WHERE project_id = $1 ORDER BY ${orderBy}`;\n const params: unknown[] = [projectId];\n if (opts.limit !== undefined) {\n params.push(opts.limit);\n sql += ` LIMIT $${params.length}`;\n }\n const r = await this.pool.query(sql, params);\n return r.rows.map(mapSession);\n }\n\n async listSessions(opts: ListSessionsOptions = {}): Promise<SessionWithProject[]> {\n const where: string[] = [];\n const params: unknown[] = [];\n if (opts.projectId !== undefined) {\n params.push(opts.projectId);\n where.push(`s.project_id = $${params.length}`);\n }\n if (opts.status) {\n params.push(opts.status);\n where.push(`s.status = $${params.length}`);\n }\n let sql = `\n SELECT s.*, p.slug AS project_slug, p.display_name AS project_name\n FROM sessions s JOIN projects p ON p.id = s.project_id\n `;\n if (where.length) sql += \" WHERE \" + where.join(\" AND \");\n sql += \" ORDER BY s.date DESC, s.number DESC\";\n params.push(opts.limit ?? 20);\n sql += ` LIMIT $${params.length}`;\n const r = await this.pool.query(sql, params);\n return r.rows.map((row) => ({ ...mapSession(row), project_slug: row.project_slug as string, project_name: row.project_name as string }));\n }\n\n async findSessionByFilename(projectId: number, filename: string): Promise<{ id: number } | null> {\n const r = await this.pool.query<{ id: number }>(\n \"SELECT id FROM sessions WHERE project_id = $1 AND filename = $2 LIMIT 1\",\n [projectId, filename]\n );\n return r.rows[0] ?? null;\n }\n\n async sessionNumberTaken(projectId: number, number: number): Promise<boolean> {\n const r = await this.pool.query(\n \"SELECT 1 FROM sessions WHERE project_id = $1 AND number = $2 LIMIT 1\",\n [projectId, number]\n );\n return (r.rowCount ?? 0) > 0;\n }\n\n async createSession(input: NewSession): Promise<Session> {\n const status = input.status ?? \"completed\";\n const r = await this.pool.query(\n `INSERT INTO sessions (project_id, number, date, slug, title, filename, status, created_at)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *`,\n [input.projectId, input.number, input.date, input.slug, input.title, input.filename, status, input.createdAt]\n );\n return mapSession(r.rows[0]);\n }\n\n async upsertSessionIfAbsent(input: NewSession): Promise<boolean> {\n const existing = await this.pool.query(\n \"SELECT id FROM sessions WHERE project_id = $1 AND number = $2\",\n [input.projectId, input.number]\n );\n if ((existing.rowCount ?? 0) > 0) return false;\n\n const status = input.status ?? \"completed\";\n await this.pool.query(\n `INSERT INTO sessions (project_id, number, date, slug, title, filename, status, created_at)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,\n [input.projectId, input.number, input.date, input.slug, input.title, input.filename, status, input.createdAt]\n );\n return true;\n }\n\n async updateSessionMeta(\n id: number,\n patch: { slug?: string; title?: string; filename?: string }\n ): Promise<void> {\n const sets: string[] = [];\n const params: unknown[] = [];\n if (patch.slug !== undefined) {\n params.push(patch.slug);\n sets.push(`slug = $${params.length}`);\n }\n if (patch.title !== undefined) {\n params.push(patch.title);\n sets.push(`title = $${params.length}`);\n }\n if (patch.filename !== undefined) {\n params.push(patch.filename);\n sets.push(`filename = $${params.length}`);\n }\n if (!sets.length) return;\n params.push(id);\n await this.pool.query(`UPDATE sessions SET ${sets.join(\", \")} WHERE id = $${params.length}`, params);\n }\n\n async updateSessionNumber(id: number, number: number, opts: { filename?: string } = {}): Promise<void> {\n if (opts.filename !== undefined) {\n await this.pool.query(\"UPDATE sessions SET number = $1, filename = $2 WHERE id = $3\", [\n number,\n opts.filename,\n id,\n ]);\n } else {\n await this.pool.query(\"UPDATE sessions SET number = $1 WHERE id = $2\", [number, id]);\n }\n }\n\n async updateSessionFilename(id: number, filename: string): Promise<void> {\n await this.pool.query(\"UPDATE sessions SET filename = $1 WHERE id = $2\", [filename, id]);\n }\n\n async updateSessionStatus(id: number, status: SessionStatus, opts: { closedAt?: number } = {}): Promise<void> {\n if (opts.closedAt !== undefined) {\n await this.pool.query(\"UPDATE sessions SET status = $1, closed_at = $2 WHERE id = $3\", [\n status,\n opts.closedAt,\n id,\n ]);\n } else {\n await this.pool.query(\"UPDATE sessions SET status = $1 WHERE id = $2\", [status, id]);\n }\n }\n\n async moveSessionToProject(id: number, projectId: number, opts: { number?: number } = {}): Promise<void> {\n if (opts.number !== undefined) {\n await this.pool.query(\"UPDATE sessions SET project_id = $1, number = $2 WHERE id = $3\", [\n projectId,\n opts.number,\n id,\n ]);\n } else {\n await this.pool.query(\"UPDATE sessions SET project_id = $1 WHERE id = $2\", [projectId, id]);\n }\n }\n\n async deleteSession(id: number): Promise<void> {\n await this.pool.query(\"DELETE FROM sessions WHERE id = $1\", [id]);\n }\n\n async backfillFoldedSession(keepId: number, dropId: number): Promise<void> {\n await this.pool.query(\n `UPDATE sessions SET\n claude_session_id = COALESCE(claude_session_id,\n (SELECT claude_session_id FROM sessions WHERE id = $1)),\n token_count = COALESCE(token_count,\n (SELECT token_count FROM sessions WHERE id = $1)),\n closed_at = COALESCE(closed_at,\n (SELECT closed_at FROM sessions WHERE id = $1)),\n status = CASE WHEN status = 'open'\n AND (SELECT status FROM sessions WHERE id = $1) != 'open'\n THEN (SELECT status FROM sessions WHERE id = $1)\n ELSE status END\n WHERE id = $2`,\n [dropId, keepId]\n );\n }\n\n async countSessionsForProject(projectId: number): Promise<number> {\n const r = await this.pool.query<{ n: string }>(\n \"SELECT COUNT(*)::text AS n FROM sessions WHERE project_id = $1\",\n [projectId]\n );\n return parseInt(r.rows[0].n, 10);\n }\n\n async countSessions(): Promise<number> {\n const r = await this.pool.query<{ n: string }>(\"SELECT COUNT(*)::text AS n FROM sessions\");\n return parseInt(r.rows[0].n, 10);\n }\n\n async getMostRecentSessionDate(projectId: number): Promise<string | null> {\n const r = await this.pool.query<{ date: string }>(\n \"SELECT date FROM sessions WHERE project_id = $1 ORDER BY date DESC LIMIT 1\",\n [projectId]\n );\n return r.rows[0]?.date ?? null;\n }\n\n async getMostRecentSessionCreatedAt(): Promise<number | null> {\n const r = await this.pool.query<{ created_at: string }>(\n \"SELECT created_at FROM sessions ORDER BY created_at DESC LIMIT 1\"\n );\n return r.rows[0] ? Number(r.rows[0].created_at) : null;\n }\n\n // -------------------------------------------------------------------------\n // Session tags\n // -------------------------------------------------------------------------\n\n async listSessionTagIds(sessionId: number): Promise<number[]> {\n const r = await this.pool.query<{ tag_id: number }>(\n \"SELECT tag_id FROM session_tags WHERE session_id = $1\",\n [sessionId]\n );\n return r.rows.map((row) => row.tag_id);\n }\n\n async sessionHasTag(sessionId: number, tagId: number): Promise<boolean> {\n const r = await this.pool.query(\"SELECT 1 FROM session_tags WHERE session_id = $1 AND tag_id = $2\", [\n sessionId,\n tagId,\n ]);\n return (r.rowCount ?? 0) > 0;\n }\n\n async addSessionTag(sessionId: number, tagId: number): Promise<void> {\n await this.pool.query(\"INSERT INTO session_tags (session_id, tag_id) VALUES ($1, $2)\", [sessionId, tagId]);\n }\n\n async deleteSessionTag(sessionId: number, tagId: number): Promise<void> {\n await this.pool.query(\"DELETE FROM session_tags WHERE session_id = $1 AND tag_id = $2\", [sessionId, tagId]);\n }\n\n async reassignSessionTag(fromSessionId: number, toSessionId: number, tagId: number): Promise<void> {\n await this.pool.query(\n \"UPDATE session_tags SET session_id = $1 WHERE session_id = $2 AND tag_id = $3\",\n [toSessionId, fromSessionId, tagId]\n );\n }\n\n async listTagsForSession(sessionId: number): Promise<string[]> {\n const r = await this.pool.query<{ name: string }>(\n \"SELECT t.name FROM tags t JOIN session_tags st ON st.tag_id = t.id WHERE st.session_id = $1 ORDER BY t.name\",\n [sessionId]\n );\n return r.rows.map((row) => row.name);\n }\n\n // -------------------------------------------------------------------------\n // Links\n // -------------------------------------------------------------------------\n\n async addLink(input: {\n sessionId: number;\n targetProjectId: number;\n linkType: LinkType;\n createdAt: number;\n }): Promise<void> {\n await this.pool.query(\n \"INSERT INTO links (session_id, target_project_id, link_type, created_at) VALUES ($1, $2, $3, $4)\",\n [input.sessionId, input.targetProjectId, input.linkType, input.createdAt]\n );\n }\n\n async listLinksForSession(sessionId: number): Promise<Array<{ id: number; target_project_id: number }>> {\n const r = await this.pool.query(\"SELECT id, target_project_id FROM links WHERE session_id = $1\", [\n sessionId,\n ]);\n return r.rows as Array<{ id: number; target_project_id: number }>;\n }\n\n async listLinksForProject(projectId: number): Promise<Array<{ id: number; session_id: number }>> {\n const r = await this.pool.query(\"SELECT id, session_id FROM links WHERE target_project_id = $1\", [\n projectId,\n ]);\n return r.rows as Array<{ id: number; session_id: number }>;\n }\n\n async linkExists(sessionId: number, targetProjectId: number): Promise<boolean> {\n const r = await this.pool.query(\n \"SELECT 1 FROM links WHERE session_id = $1 AND target_project_id = $2 LIMIT 1\",\n [sessionId, targetProjectId]\n );\n return (r.rowCount ?? 0) > 0;\n }\n\n async deleteLink(id: number): Promise<void> {\n await this.pool.query(\"DELETE FROM links WHERE id = $1\", [id]);\n }\n\n async deleteLinksTargetingProject(projectId: number): Promise<void> {\n await this.pool.query(\"DELETE FROM links WHERE target_project_id = $1\", [projectId]);\n }\n\n async deleteLinksFromProjectSessions(projectId: number): Promise<void> {\n await this.pool.query(\n \"DELETE FROM links WHERE session_id IN (SELECT id FROM sessions WHERE project_id = $1)\",\n [projectId]\n );\n }\n\n async retargetLink(id: number, targetProjectId: number): Promise<void> {\n await this.pool.query(\"UPDATE links SET target_project_id = $1 WHERE id = $2\", [targetProjectId, id]);\n }\n\n async moveLinkToSession(id: number, sessionId: number): Promise<void> {\n await this.pool.query(\"UPDATE links SET session_id = $1 WHERE id = $2\", [sessionId, id]);\n }\n\n async reassignLinksTarget(fromProjectId: number, toProjectId: number): Promise<void> {\n // Postgres UPDATE has no OR IGNORE; skip rows that would collide with an\n // existing (session_id, target_project_id) row, matching SQLite's behaviour\n // of silently leaving those rows for the caller's follow-up cleanup.\n await this.pool.query(\n `UPDATE links SET target_project_id = $1 WHERE target_project_id = $2\n AND NOT EXISTS (\n SELECT 1 FROM links l2 WHERE l2.session_id = links.session_id AND l2.target_project_id = $1\n )`,\n [toProjectId, fromProjectId]\n );\n }\n\n async countLinksForProject(projectId: number): Promise<number> {\n const r = await this.pool.query<{ n: string }>(\n \"SELECT COUNT(*)::text AS n FROM links WHERE target_project_id = $1\",\n [projectId]\n );\n return parseInt(r.rows[0].n, 10);\n }\n\n async deleteSelfLinksForProject(projectId: number): Promise<void> {\n await this.pool.query(\n `DELETE FROM links WHERE target_project_id = $1\n AND session_id IN (SELECT id FROM sessions WHERE project_id = $2)`,\n [projectId, projectId]\n );\n }\n\n // -------------------------------------------------------------------------\n // Compaction log\n // -------------------------------------------------------------------------\n\n async appendCompactionLog(entry: NewCompactionLogEntry): Promise<void> {\n await this.pool.query(\n `INSERT INTO compaction_log (project_id, session_id, trigger, files_written, token_count, created_at)\n VALUES ($1, $2, $3, $4, $5, $6)`,\n [entry.projectId, entry.sessionId, entry.trigger, entry.filesWritten, entry.tokenCount, entry.createdAt]\n );\n }\n\n async countCompactionLogsForProject(projectId: number): Promise<number> {\n const r = await this.pool.query<{ n: string }>(\n \"SELECT COUNT(*)::text AS n FROM compaction_log WHERE project_id = $1\",\n [projectId]\n );\n return parseInt(r.rows[0].n, 10);\n }\n\n async moveCompactionLogsByProject(fromProjectId: number, toProjectId: number): Promise<number> {\n const r = await this.pool.query(\"UPDATE compaction_log SET project_id = $1 WHERE project_id = $2\", [\n toProjectId,\n fromProjectId,\n ]);\n return r.rowCount ?? 0;\n }\n\n async moveCompactionLogsBySession(fromSessionId: number, toSessionId: number): Promise<void> {\n await this.pool.query(\"UPDATE compaction_log SET session_id = $1 WHERE session_id = $2\", [\n toSessionId,\n fromSessionId,\n ]);\n }\n\n async deleteCompactionLogsForProject(projectId: number): Promise<void> {\n await this.pool.query(\"DELETE FROM compaction_log WHERE project_id = $1\", [projectId]);\n }\n\n // -------------------------------------------------------------------------\n // Project merge\n // -------------------------------------------------------------------------\n\n async planProjectMerge(fromSlug: string, intoSlug: string): Promise<MergePlan> {\n const { MergeError } = await import(\"../registry/merge.js\");\n if (fromSlug === intoSlug) {\n throw new MergeError(`Cannot merge ${fromSlug} into itself.`);\n }\n\n const fromR = await this.pool.query<{ id: number; slug: string }>(\n \"SELECT id, slug FROM projects WHERE slug = $1\",\n [fromSlug]\n );\n const intoR = await this.pool.query<{ id: number; slug: string }>(\n \"SELECT id, slug FROM projects WHERE slug = $1\",\n [intoSlug]\n );\n const from = fromR.rows[0];\n const into = intoR.rows[0];\n if (!from) throw new MergeError(`No project with slug \"${fromSlug}\".`);\n if (!into) throw new MergeError(`No project with slug \"${intoSlug}\".`);\n\n const maxRow = await this.pool.query<{ n: number }>(\n \"SELECT COALESCE(MAX(number), 0) AS n FROM sessions WHERE project_id = $1\",\n [into.id]\n );\n let next = maxRow.rows[0].n;\n\n const incoming = await this.pool.query<{ id: number; number: number }>(\n \"SELECT id, number FROM sessions WHERE project_id = $1 ORDER BY number ASC\",\n [from.id]\n );\n const sessions = incoming.rows.map((s) => ({ id: s.id, from: s.number, to: ++next }));\n\n const count = async (sql: string, ...args: unknown[]): Promise<number> => {\n const r = await this.pool.query<{ n: string }>(sql, args);\n return parseInt(r.rows[0].n, 10);\n };\n\n const aliasTaken =\n (await count(\"SELECT COUNT(*)::text AS n FROM aliases WHERE alias = $1\", from.slug)) > 0 ||\n (await count(\"SELECT COUNT(*)::text AS n FROM projects WHERE slug = $1\", from.slug)) > 1;\n\n return {\n fromId: from.id,\n fromSlug: from.slug,\n intoId: into.id,\n intoSlug: into.slug,\n sessions,\n tags: await count(\"SELECT COUNT(*)::text AS n FROM project_tags WHERE project_id = $1\", from.id),\n aliases: await count(\"SELECT COUNT(*)::text AS n FROM aliases WHERE project_id = $1\", from.id),\n compactions: await count(\n \"SELECT COUNT(*)::text AS n FROM compaction_log WHERE project_id = $1\",\n from.id\n ),\n links: await count(\"SELECT COUNT(*)::text AS n FROM links WHERE target_project_id = $1\", from.id),\n aliasToAdd: aliasTaken ? undefined : from.slug,\n };\n }\n\n async applyProjectMerge(plan: MergePlan): Promise<void> {\n const client: PoolClient = await this.pool.connect();\n try {\n await client.query(\"BEGIN\");\n\n for (const s of plan.sessions) {\n await client.query(\"UPDATE sessions SET project_id = $1, number = $2 WHERE id = $3\", [\n plan.intoId,\n s.to,\n s.id,\n ]);\n }\n\n await client.query(\n `INSERT INTO project_tags (project_id, tag_id)\n SELECT $1, tag_id FROM project_tags WHERE project_id = $2\n ON CONFLICT (project_id, tag_id) DO NOTHING`,\n [plan.intoId, plan.fromId]\n );\n await client.query(\"DELETE FROM project_tags WHERE project_id = $1\", [plan.fromId]);\n\n await client.query(\"UPDATE aliases SET project_id = $1 WHERE project_id = $2\", [\n plan.intoId,\n plan.fromId,\n ]);\n\n await client.query(\"UPDATE compaction_log SET project_id = $1 WHERE project_id = $2\", [\n plan.intoId,\n plan.fromId,\n ]);\n\n // Links: same NOT EXISTS guard as reassignLinksTarget, plus the same\n // two follow-up cleanups SQLite's applyMerge does.\n await client.query(\n `UPDATE links SET target_project_id = $1 WHERE target_project_id = $2\n AND NOT EXISTS (\n SELECT 1 FROM links l2 WHERE l2.session_id = links.session_id AND l2.target_project_id = $1\n )`,\n [plan.intoId, plan.fromId]\n );\n await client.query(\"DELETE FROM links WHERE target_project_id = $1\", [plan.fromId]);\n await client.query(\n `DELETE FROM links WHERE target_project_id = $1\n AND session_id IN (SELECT id FROM sessions WHERE project_id = $2)`,\n [plan.intoId, plan.intoId]\n );\n\n if (plan.aliasToAdd) {\n await client.query(\n \"INSERT INTO aliases (alias, project_id) VALUES ($1, $2) ON CONFLICT (alias) DO NOTHING\",\n [plan.aliasToAdd, plan.intoId]\n );\n }\n\n await client.query(\"DELETE FROM projects WHERE id = $1\", [plan.fromId]);\n\n await client.query(\"COMMIT\");\n } catch (e) {\n await client.query(\"ROLLBACK\");\n throw e;\n } finally {\n client.release();\n }\n }\n}\n"],"mappings":"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"}