@straiffi/archon 1.8.6 → 1.8.8

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 (169) hide show
  1. package/dist/client/assets/ActivityView--4X_m6mR.js +1 -0
  2. package/dist/client/assets/GitHubActionsDialog-DZ1lHM01.js +1 -0
  3. package/dist/client/assets/RepositoryGraphDialog-B--YlQcV.js +1 -0
  4. package/dist/client/assets/VerificationDialog-BIWPtBg3.js +4 -0
  5. package/dist/client/assets/{arc-D2Nn9xIj.js → arc-C83vRtlz.js} +1 -1
  6. package/dist/client/assets/architecture-7EHR7CIX-Dm8H7N6Z.js +1 -0
  7. package/dist/client/assets/{architectureDiagram-3BPJPVTR-BH5LLK54.js → architectureDiagram-3BPJPVTR-CY5xTOZJ.js} +1 -1
  8. package/dist/client/assets/{blockDiagram-GPEHLZMM-xOxShPwZ.js → blockDiagram-GPEHLZMM-DkMi7gMh.js} +1 -1
  9. package/dist/client/assets/{c4Diagram-AAUBKEIU-BXMrXnKR.js → c4Diagram-AAUBKEIU-ZZxXBBU4.js} +1 -1
  10. package/dist/client/assets/channel-b2AN-U3x.js +1 -0
  11. package/dist/client/assets/{chunk-2J33WTMH-BdDEtNMz.js → chunk-2J33WTMH-C9PiKMs-.js} +1 -1
  12. package/dist/client/assets/{chunk-3OPIFGDE-PtSkTtIU.js → chunk-3OPIFGDE-B81aUmvi.js} +1 -1
  13. package/dist/client/assets/{chunk-4BX2VUAB-CtLS8D71.js → chunk-4BX2VUAB-C5pU9ApX.js} +1 -1
  14. package/dist/client/assets/{chunk-4EGX6M5U-Btnm0QNe.js → chunk-4EGX6M5U-CAjWPjND.js} +1 -1
  15. package/dist/client/assets/{chunk-55IACEB6-74CcJaub.js → chunk-55IACEB6-DhO4-AG4.js} +1 -1
  16. package/dist/client/assets/{chunk-5DO6E6H7-WLZ8L4ko.js → chunk-5DO6E6H7-BDKA-zHS.js} +1 -1
  17. package/dist/client/assets/{chunk-5ZQYHXKU-D5PYP24m.js → chunk-5ZQYHXKU-Bk2Owm90.js} +1 -1
  18. package/dist/client/assets/{chunk-727SXJPM-gDX0xZh1.js → chunk-727SXJPM-Bx-79aJk.js} +1 -1
  19. package/dist/client/assets/{chunk-AQP2D5EJ-Bat82lQQ.js → chunk-AQP2D5EJ-GQNXYOf0.js} +1 -1
  20. package/dist/client/assets/{chunk-BR22UD5L-Cou5beGe.js → chunk-BR22UD5L-12D2GWh4.js} +1 -1
  21. package/dist/client/assets/{chunk-BSJP7CBP-DJ57XcV5.js → chunk-BSJP7CBP-DMi77e84.js} +1 -1
  22. package/dist/client/assets/{chunk-CSCIHK7Q-D3sau006.js → chunk-CSCIHK7Q-BV22j6vn.js} +2 -2
  23. package/dist/client/assets/{chunk-FHYWG6QK-Ba10e2kj.js → chunk-FHYWG6QK-BhSajKiB.js} +1 -1
  24. package/dist/client/assets/{chunk-FMBD7UC4-CEo2wZMx.js → chunk-FMBD7UC4-Drmlc0wM.js} +1 -1
  25. package/dist/client/assets/{chunk-KSCS5N6A-CqEcB7Dr.js → chunk-KSCS5N6A-C9LlcbWv.js} +1 -1
  26. package/dist/client/assets/{chunk-L5ZTLDWV-TRTuwXsF.js → chunk-L5ZTLDWV-D8PJqJnh.js} +1 -1
  27. package/dist/client/assets/{chunk-LZXEDZCA-DdpNmv37.js → chunk-LZXEDZCA-DcbPPozR.js} +2 -2
  28. package/dist/client/assets/{chunk-MPE355IW-MqEt-J8G.js → chunk-MPE355IW-Cw8YmjTC.js} +1 -1
  29. package/dist/client/assets/{chunk-MZUSXYTE-DdNi9lhx.js → chunk-MZUSXYTE-jvxRCZ7x.js} +1 -1
  30. package/dist/client/assets/{chunk-N66VUXT2-CjemTW4E.js → chunk-N66VUXT2-00i4y0yD.js} +1 -1
  31. package/dist/client/assets/{chunk-ND2GUHAM-BCUL9zWr.js → chunk-ND2GUHAM-NV-_2KqZ.js} +1 -1
  32. package/dist/client/assets/{chunk-NZK2D7GU-BAxuwE_2.js → chunk-NZK2D7GU-CbqA26gb.js} +1 -1
  33. package/dist/client/assets/{chunk-O5CBEL6O-Cttpc9yf.js → chunk-O5CBEL6O-B0DMN44B.js} +1 -1
  34. package/dist/client/assets/{chunk-PUPMXCY4-ZvwTw0To.js → chunk-PUPMXCY4-DfWourL7.js} +1 -1
  35. package/dist/client/assets/chunk-QZHKN3VN-CtbsU1Bj.js +1 -0
  36. package/dist/client/assets/{chunk-UIBZB4QT-BTY9be9s.js → chunk-UIBZB4QT-v-a5ZfN6.js} +1 -1
  37. package/dist/client/assets/{chunk-WCWK7LTN-Db1n3n3-.js → chunk-WCWK7LTN-CtYzhvvb.js} +1 -1
  38. package/dist/client/assets/chunk-WU5MYG2G-CYpcnwf9.js +1 -0
  39. package/dist/client/assets/{chunk-XPW4576I-DObFPxjB.js → chunk-XPW4576I-CvnAuXH2.js} +1 -1
  40. package/dist/client/assets/classDiagram-4FO5ZUOK-C0exS5oM.js +1 -0
  41. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DkB9dQZ1.js +1 -0
  42. package/dist/client/assets/{cose-bilkent-S5V4N54A-MlOPiG6P.js → cose-bilkent-S5V4N54A-fchD-dH2.js} +1 -1
  43. package/dist/client/assets/{dagre-BM42HDAG-BTsZ2dQI.js → dagre-BM42HDAG-B5HD9fyK.js} +1 -1
  44. package/dist/client/assets/{dagre-BWNHuDdf.js → dagre-DSrzm-Gq.js} +1 -1
  45. package/dist/client/assets/{diagram-2AECGRRQ-afPu4kp4.js → diagram-2AECGRRQ-BCXvWFpM.js} +1 -1
  46. package/dist/client/assets/{diagram-5GNKFQAL-SixbJGPN.js → diagram-5GNKFQAL-CSpWGfAO.js} +1 -1
  47. package/dist/client/assets/{diagram-KO2AKTUF-Dk1670ag.js → diagram-KO2AKTUF-CFECGppT.js} +1 -1
  48. package/dist/client/assets/{diagram-LMA3HP47-BDNhjRaO.js → diagram-LMA3HP47-Dpb99QbZ.js} +1 -1
  49. package/dist/client/assets/{diagram-OG6HWLK6-BLowQCLY.js → diagram-OG6HWLK6-DJcTGnx0.js} +1 -1
  50. package/dist/client/assets/{erDiagram-TEJ5UH35-DAg9kUbm.js → erDiagram-TEJ5UH35-jWArJxNU.js} +1 -1
  51. package/dist/client/assets/eventmodeling-FCH6USID-BWhw0UJ8.js +1 -0
  52. package/dist/client/assets/{flowDiagram-I6XJVG4X-BsaPmiIS.js → flowDiagram-I6XJVG4X-DDF3P1hY.js} +1 -1
  53. package/dist/client/assets/{ganttDiagram-6RSMTGT7-DfF1TT-s.js → ganttDiagram-6RSMTGT7-Corwr_18.js} +1 -1
  54. package/dist/client/assets/gitGraph-WXDBUCRP-BSnxAa1J.js +1 -0
  55. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CEvTiN6n.js → gitGraphDiagram-PVQCEYII-WzlpszWn.js} +1 -1
  56. package/dist/client/assets/index-Bs5SQZhz.css +2 -0
  57. package/dist/client/assets/index-Dy-7h3lN.js +274 -0
  58. package/dist/client/assets/info-J43DQDTF-DcFwF1-h.js +1 -0
  59. package/dist/client/assets/{infoDiagram-5YYISTIA-CBjYYrzJ.js → infoDiagram-5YYISTIA-C0VwTwNC.js} +1 -1
  60. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-BuCkp1uL.js → ishikawaDiagram-YF4QCWOH-m9l3k2qX.js} +1 -1
  61. package/dist/client/assets/{journeyDiagram-JHISSGLW-DkUPCxT5.js → journeyDiagram-JHISSGLW-DMhT8HTW.js} +1 -1
  62. package/dist/client/assets/{kanban-definition-UN3LZRKU-CXwNZ0Fa.js → kanban-definition-UN3LZRKU-C2ZkcOi0.js} +1 -1
  63. package/dist/client/assets/{line-CRTZgJXA.js → line-gKkUdUWR.js} +1 -1
  64. package/dist/client/assets/{linear-DT4rSZvy.js → linear-BOwW57yX.js} +1 -1
  65. package/dist/client/assets/{mermaid-parser.core-NTByLn30.js → mermaid-parser.core-DdBfJWbP.js} +2 -2
  66. package/dist/client/assets/{mermaid.core-DszsB_Gl.js → mermaid.core-DXnhq-eu.js} +3 -3
  67. package/dist/client/assets/{mindmap-definition-RKZ34NQL-DCrqCcPX.js → mindmap-definition-RKZ34NQL-B0gX4aJy.js} +1 -1
  68. package/dist/client/assets/{ordinal-k--hYEme.js → ordinal-CZ33lFpK.js} +1 -1
  69. package/dist/client/assets/packet-YPE3B663-1VZiO_9r.js +1 -0
  70. package/dist/client/assets/pie-LRSECV5Y-BX1-tQCu.js +1 -0
  71. package/dist/client/assets/{pieDiagram-4H26LBE5-CbHeTKto.js → pieDiagram-4H26LBE5-BHYdLmpx.js} +1 -1
  72. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-Ptpkh0Dc.js → quadrantDiagram-W4KKPZXB-C-H-yQwU.js} +1 -1
  73. package/dist/client/assets/radar-GUYGQ44K-B2HRrCR4.js +1 -0
  74. package/dist/client/assets/{requirementDiagram-4Y6WPE33-73PM-Ae5.js → requirementDiagram-4Y6WPE33-DY_pSCFy.js} +1 -1
  75. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-B7RCxzNy.js → sankeyDiagram-5OEKKPKP-CC61HRGf.js} +1 -1
  76. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-DGXfHCo0.js → sequenceDiagram-3UESZ5HK-B8fXWNxw.js} +1 -1
  77. package/dist/client/assets/{stateDiagram-AJRCARHV-Cl0_SVL3.js → stateDiagram-AJRCARHV-C_VYKsXW.js} +1 -1
  78. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Ctk1r914.js +1 -0
  79. package/dist/client/assets/{timeline-definition-PNZ67QCA-Cufq-wYg.js → timeline-definition-PNZ67QCA-0fB4PIIX.js} +1 -1
  80. package/dist/client/assets/treeView-BLDUP644-CDfUVbUi.js +1 -0
  81. package/dist/client/assets/treemap-LRROVOQU-DUWqu8sK.js +1 -0
  82. package/dist/client/assets/{vennDiagram-CIIHVFJN-ROVZhhP-.js → vennDiagram-CIIHVFJN-CfpVF7Aa.js} +1 -1
  83. package/dist/client/assets/wardley-L42UT6IY-BEFuPUCy.js +1 -0
  84. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DlHRfLPH.js → wardleyDiagram-YWT4CUSO-CgdK4FcC.js} +1 -1
  85. package/dist/client/assets/{xychartDiagram-2RQKCTM6-DW-KpRbf.js → xychartDiagram-2RQKCTM6-BEfatQYR.js} +1 -1
  86. package/dist/client/index.html +2 -2
  87. package/dist/server/db.js +399 -0
  88. package/dist/server/db.js.map +1 -1
  89. package/dist/server/index.js +106 -46
  90. package/dist/server/index.js.map +1 -1
  91. package/dist/server/lib/agent.js +9 -6
  92. package/dist/server/lib/agent.js.map +1 -1
  93. package/dist/server/lib/checkWorkspaceIdentity.js +2 -206
  94. package/dist/server/lib/checkWorkspaceIdentity.js.map +1 -1
  95. package/dist/server/lib/executionReconciliation.js +73 -0
  96. package/dist/server/lib/executionReconciliation.js.map +1 -1
  97. package/dist/server/lib/git.js +302 -11
  98. package/dist/server/lib/git.js.map +1 -1
  99. package/dist/server/lib/integrations/github.js +145 -66
  100. package/dist/server/lib/integrations/github.js.map +1 -1
  101. package/dist/server/lib/integrations/importedIssueImages.js +10 -15
  102. package/dist/server/lib/integrations/importedIssueImages.js.map +1 -1
  103. package/dist/server/lib/postBuildChecks.js +20 -12
  104. package/dist/server/lib/postBuildChecks.js.map +1 -1
  105. package/dist/server/lib/repositoryGraphAnalyzer.js +392 -0
  106. package/dist/server/lib/repositoryGraphAnalyzer.js.map +1 -0
  107. package/dist/server/lib/repositoryGraphGitHistory.js +371 -0
  108. package/dist/server/lib/repositoryGraphGitHistory.js.map +1 -0
  109. package/dist/server/lib/repositoryGraphIdentity.js +134 -0
  110. package/dist/server/lib/repositoryGraphIdentity.js.map +1 -0
  111. package/dist/server/lib/repositoryGraphManifest.js +386 -0
  112. package/dist/server/lib/repositoryGraphManifest.js.map +1 -0
  113. package/dist/server/lib/repositoryGraphQueries.js +370 -0
  114. package/dist/server/lib/repositoryGraphQueries.js.map +1 -0
  115. package/dist/server/lib/repositoryGraphRetention.js +104 -0
  116. package/dist/server/lib/repositoryGraphRetention.js.map +1 -0
  117. package/dist/server/lib/repositoryGraphRoutes.js +145 -0
  118. package/dist/server/lib/repositoryGraphRoutes.js.map +1 -0
  119. package/dist/server/lib/repositoryGraphScan.js +370 -0
  120. package/dist/server/lib/repositoryGraphScan.js.map +1 -0
  121. package/dist/server/lib/repositoryGraphStore.js +352 -0
  122. package/dist/server/lib/repositoryGraphStore.js.map +1 -0
  123. package/dist/server/lib/repositoryGraphTargets.js +30 -0
  124. package/dist/server/lib/repositoryGraphTargets.js.map +1 -0
  125. package/dist/server/lib/repositoryGraphTypeScript.js +288 -0
  126. package/dist/server/lib/repositoryGraphTypeScript.js.map +1 -0
  127. package/dist/server/lib/terminal.js +25 -1
  128. package/dist/server/lib/terminal.js.map +1 -1
  129. package/dist/server/lib/types.js +1 -0
  130. package/dist/server/lib/types.js.map +1 -1
  131. package/dist/server/lib/workspaceIdentity.js +433 -0
  132. package/dist/server/lib/workspaceIdentity.js.map +1 -0
  133. package/dist/server/workers/plan.js +9 -7
  134. package/dist/server/workers/plan.js.map +1 -1
  135. package/dist/server/workers/repositoryGraphAnalyzer.js +282 -0
  136. package/dist/server/workers/repositoryGraphAnalyzer.js.map +1 -0
  137. package/package.json +2 -1
  138. package/dist/client/assets/ActivityView-DDeEQgbm.js +0 -1
  139. package/dist/client/assets/GitHubActionsDialog-DH-B_LR3.js +0 -1
  140. package/dist/client/assets/VerificationDialog-x43F7tbo.js +0 -4
  141. package/dist/client/assets/architecture-7EHR7CIX-C-4Cp4ml.js +0 -1
  142. package/dist/client/assets/channel-C0dk5Jiy.js +0 -1
  143. package/dist/client/assets/chunk-QZHKN3VN-LI_RZca2.js +0 -1
  144. package/dist/client/assets/chunk-WU5MYG2G-620NtSlY.js +0 -1
  145. package/dist/client/assets/classDiagram-4FO5ZUOK-C5ptkk7I.js +0 -1
  146. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BFuXUVOx.js +0 -1
  147. package/dist/client/assets/eventmodeling-FCH6USID-Dqb6QKoI.js +0 -1
  148. package/dist/client/assets/gitGraph-WXDBUCRP-DYEsqh93.js +0 -1
  149. package/dist/client/assets/index-B5TSPCJr.css +0 -2
  150. package/dist/client/assets/index-DV0vl670.js +0 -274
  151. package/dist/client/assets/info-J43DQDTF-CfjXFZai.js +0 -1
  152. package/dist/client/assets/packet-YPE3B663-Dv0HkRHU.js +0 -1
  153. package/dist/client/assets/pie-LRSECV5Y-S5zPzZFN.js +0 -1
  154. package/dist/client/assets/radar-GUYGQ44K-B7psLv22.js +0 -1
  155. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CqzC1yAT.js +0 -1
  156. package/dist/client/assets/treeView-BLDUP644-B8H0kT67.js +0 -1
  157. package/dist/client/assets/treemap-LRROVOQU-DPLhFHnh.js +0 -1
  158. package/dist/client/assets/wardley-L42UT6IY-B5-Vb08V.js +0 -1
  159. /package/dist/client/assets/{array-BNor45A1.js → array-B2f2amhX.js} +0 -0
  160. /package/dist/client/assets/{chunk-NNHCCRGN-CZB-V-Gv.js → chunk-NNHCCRGN-DgKrp8T-.js} +0 -0
  161. /package/dist/client/assets/{cytoscape.esm-B2lJcMSI.js → cytoscape.esm-BInZCgaJ.js} +0 -0
  162. /package/dist/client/assets/{defaultLocale-DPzUsThw.js → defaultLocale-sPXJ5urm.js} +0 -0
  163. /package/dist/client/assets/{dist-CERhmQhR.js → dist-DfHDMDTt.js} +0 -0
  164. /package/dist/client/assets/{graphlib-BHOW-eWz.js → graphlib-DacNv4Jd.js} +0 -0
  165. /package/dist/client/assets/{init-C0L3woqb.js → init-BVCnG1lj.js} +0 -0
  166. /package/dist/client/assets/{katex-CpjQsUQe.js → katex-BlkrXOwm.js} +0 -0
  167. /package/dist/client/assets/{path-sMK4d_s9.js → path-Bw5AvBNC.js} +0 -0
  168. /package/dist/client/assets/{rough.esm-BoTisKeL.js → rough.esm-DeLgKbOI.js} +0 -0
  169. /package/dist/client/assets/{src-BRjoYtUW.js → src-CFGsdLiG.js} +0 -0
@@ -8,6 +8,8 @@ import { createHash, randomUUID } from 'crypto';
8
8
  import cors from 'cors';
9
9
  import { adjectives, animals, uniqueNamesGenerator } from 'unique-names-generator';
10
10
  import db from './db.js';
11
+ import { pruneAllRepositoryGraphScans } from './lib/repositoryGraphRetention.js';
12
+ import { registerRepositoryGraphRoutes } from './lib/repositoryGraphRoutes.js';
11
13
  import { deleteChatSession, getChatSession, listChatSessions } from './lib/chats.js';
12
14
  import { deleteSpecSession, getCreatedTicketIdsForSnapshot, getSpecSession, getSpecSnapshot, listSpecSessions } from './lib/specs.js';
13
15
  import { createChatAttachment, createSpecAttachment, getAttachmentById, deleteChatAttachment, deleteProjectAttachments, deleteSpecSessionAttachment, getChatAttachmentForSession, getChatAttachmentLinkedMessageId, getSpecAttachmentForSession, getSpecAttachmentLinkedMessageId, MAX_CHAT_ATTACHMENTS_PER_TURN, MAX_CHAT_ATTACHMENT_SIZE_BYTES, MAX_SPEC_ATTACHMENTS_PER_TURN, MAX_SPEC_ATTACHMENT_SIZE_BYTES, readChatAttachmentContent, readSpecAttachmentContent, validateImageAttachment, } from './lib/attachments.js';
@@ -19,7 +21,7 @@ import { resolveSessionTargetForExecution } from './lib/sessionTargets.js';
19
21
  import { createChatMessage, getChatMessage, updateChatMessageContent } from './lib/chatMessages.js';
20
22
  import { attachChatTicketProposalsToMessageContent } from './lib/chatTicketProposals.js';
21
23
  import { createCorsOriginResolver, parseCorsOrigins } from './lib/cors.js';
22
- import { abortGitOperation, branchExistsLocally, commitGitChanges, createWorktreeAsync, deleteWorktree, getBundleReviewDiffForCwd, getGitDiffData, getGitDiffFiles, getGitStatus, getGitUnifiedDiff, getLocalProjectBranch, getProjectBranch, getProjectBranchSuggestions, getSelectedGitUnifiedDiff, getUnpushedCommitDiffForCwd, getUnpushedCommitSummaryForCwd, hasKnownBranchRef, listUnpushedCommitsForCwd, pullGitBranch, pushCurrentBranch, rebaseGitBranchOntoBase, resolveExistingWorktreePath, resolveWorktreePath, squashUnpushedCommitsForCwd, summarizeTicketDiffFiles, switchProjectBranch, syncWorktreeFilesAsync, undoLatestUnpushedCommitForCwd, UnpushedCommitDiffSelectionError } from './lib/git.js';
24
+ import { abortGitOperation, branchExistsLocally, commitGitChanges, createWorktreeAsync, deleteWorktree, getBundleReviewDiffForCwd, getGitDiffDataAsync, getGitDiffFiles, getGitStatus, getGitStatusAsync, getGitUnifiedDiff, getGitUnifiedDiffAsync, getLocalProjectBranch, getProjectBranch, getProjectBranchSuggestions, getSelectedGitUnifiedDiff, getUnpushedCommitDiffForCwd, getUnpushedCommitSummaryForCwd, getUnpushedCommitSummaryForCwdAsync, hasKnownBranchRef, listUnpushedCommitsForCwd, pullGitBranch, pushCurrentBranch, rebaseGitBranchOntoBase, resolveExistingWorktreePath, resolveWorktreePath, squashUnpushedCommitsForCwd, summarizeTicketDiffFiles, switchProjectBranch, syncWorktreeFilesAsync, undoLatestUnpushedCommitForCwd, UnpushedCommitDiffSelectionError } from './lib/git.js';
23
25
  import config from './lib/config.js';
24
26
  import { autoConfigProject, ProjectAutoConfigError } from './lib/projectAutoConfig.js';
25
27
  import { analyzeRepoPath, createInitialCommit, ensureInitialCommitForWorktrees, initializeGitRepo, isInitialCommitRequiredError, } from './lib/projectRepos.js';
@@ -70,7 +72,7 @@ import { ExecutionArtifactCleanupError, reconcileExecutionArtifactRegistrationIn
70
72
  import { closeHttpServer, shutdownRealtimeServer } from './lib/shutdown.js';
71
73
  import { resolveClientBuildPaths, shouldServeClientApp, STATIC_CLIENT_DIRECTORY_OPTIONS, STATIC_CLIENT_INDEX_OPTIONS, } from './lib/staticClient.js';
72
74
  import { deleteIntegrationConnection, getGitHubConnectionConfig, getJiraConnectionConfig, listIntegrationConnections, upsertIntegrationConnection, } from './lib/integrations/index.js';
73
- import { createGitHubPullRequest, createOrFindGitHubPullRequest, findOpenGitHubPullRequest, findOpenGitHubPullRequestByHeadBranch, getGitHubPullRequestComments, getGitHubPullRequestReviews, getGitHubPullRequest, GitHubApiError, hasGitHubRemoteBranch, resolveGitHubRepository, validateGitHubConnection, } from './lib/integrations/github.js';
75
+ import { createGitHubPullRequest, createOrFindGitHubPullRequest, findOpenGitHubPullRequest, findOpenGitHubPullRequestByHeadBranch, getGitHubPullRequestComments, getGitHubPullRequestReviews, getGitHubPullRequest, GitHubApiError, hasGitHubRemoteBranch, hasGitHubRemoteBranchAsync, resolveGitHubRepository, validateGitHubConnection, } from './lib/integrations/github.js';
74
76
  import { importJiraIssue, JiraApiError, prepareJiraIssueImport, validateJiraConnection } from './lib/integrations/jira.js';
75
77
  import { importGitHubIssue, prepareGitHubIssueImport } from './lib/integrations/github-issues.js';
76
78
  import { buildImportedIssueSessionTitle, importProviderHostedIssueImages } from './lib/integrations/importedIssueImages.js';
@@ -91,6 +93,7 @@ import { getCheckAttemptDiagnosticResponse, getCheckBatchDiagnosticResponse } fr
91
93
  import { getCheckDefinitionFingerprint } from './lib/checkDefinitions.js';
92
94
  import { getCheckGateEvaluation, hydrateCheckGatePresentApplicability } from './lib/checkGateEvaluations.js';
93
95
  import { captureCheckWorkspaceIdentity } from './lib/checkWorkspaceIdentity.js';
96
+ import { hasActiveBundleRepositoryGraphScan, hasActiveProjectRepositoryGraphScan } from './lib/repositoryGraphStore.js';
94
97
  import { resolveEffectiveRequiredCheckPolicyMembership, resolveRequiredCheckPolicy } from './lib/requiredCheckPolicy.js';
95
98
  import { getBundleBuildBlockerResponse, shouldStartBundleBuildNow, transitionTicketState, } from './lib/ticketWorkflowOperations.js';
96
99
  import { submitPlanTicketFollowUp, submitTicketFollowUp, } from './lib/ticketFollowUpOperations.js';
@@ -1443,8 +1446,8 @@ const bundlePullRequestDiscoveryErrorTimestamps = new Map();
1443
1446
  const bundlePullRequestSyncInFlight = new Map();
1444
1447
  const bundlePullRequestSyncErrorTimestamps = new Map();
1445
1448
  const getBundlePullRequestDiscoveryKey = (projectId, bundleId) => `${projectId}:${bundleId}`;
1446
- const startPassiveGitHubWork = (label, callback) => {
1447
- setImmediate(() => {
1449
+ const startPassiveGitHubWork = (response, label, callback) => {
1450
+ const start = () => setImmediate(() => {
1448
1451
  try {
1449
1452
  callback();
1450
1453
  }
@@ -1452,6 +1455,11 @@ const startPassiveGitHubWork = (label, callback) => {
1452
1455
  console.warn(`[integrations] github passive work failed ${label}`, error);
1453
1456
  }
1454
1457
  });
1458
+ if (response.writableFinished) {
1459
+ start();
1460
+ return;
1461
+ }
1462
+ response.once('finish', start);
1455
1463
  };
1456
1464
  const isBundlePullRequestDiscoveryCoolingDown = (timestamps, key, cooldownMs) => {
1457
1465
  const lastAttemptAt = timestamps.get(key);
@@ -1475,9 +1483,6 @@ const shouldDiscoverBundlePullRequestOnOpen = (project, bundle) => {
1475
1483
  if (tickets.some(ticket => ticket.agent_status === 'running')) {
1476
1484
  return false;
1477
1485
  }
1478
- if (!hasGitHubRemoteBranch(project.repo_path, bundle.branch)) {
1479
- return false;
1480
- }
1481
1486
  const key = getBundlePullRequestDiscoveryKey(project.id, bundle.id);
1482
1487
  if (bundlePullRequestDiscoveryInFlight.has(key)) {
1483
1488
  return false;
@@ -1491,6 +1496,9 @@ const startBundlePullRequestDiscoveryIfNeeded = (project, bundle) => {
1491
1496
  const key = getBundlePullRequestDiscoveryKey(project.id, bundle.id);
1492
1497
  const task = (async () => {
1493
1498
  try {
1499
+ if (!await hasGitHubRemoteBranchAsync(project.repo_path, bundle.branch)) {
1500
+ return;
1501
+ }
1494
1502
  const discoveredPullRequest = await discoverOpenBundlePullRequest(project, bundle);
1495
1503
  if (!discoveredPullRequest) {
1496
1504
  bundlePullRequestDiscoveryMissTimestamps.set(key, Date.now());
@@ -1498,7 +1506,7 @@ const startBundlePullRequestDiscoveryIfNeeded = (project, bundle) => {
1498
1506
  }
1499
1507
  bundlePullRequestDiscoveryMissTimestamps.delete(key);
1500
1508
  bundlePullRequestDiscoveryErrorTimestamps.delete(key);
1501
- reconcileBundleTicketsAfterPullRequestSync(project.id, bundle.id);
1509
+ emitBundlePullRequestUpdated(project.id, bundle.id);
1502
1510
  }
1503
1511
  catch (error) {
1504
1512
  bundlePullRequestDiscoveryErrorTimestamps.set(key, Date.now());
@@ -1587,9 +1595,10 @@ const startTrackedBundlePullRequestSyncIfNeeded = (project, bundle, options = {}
1587
1595
  const key = getBundlePullRequestDiscoveryKey(project.id, bundle.id);
1588
1596
  const task = (async () => {
1589
1597
  try {
1590
- await syncTrackedBundlePullRequestIfNeeded(project, bundle, { ...options, rethrow: true });
1598
+ const nextPullRequest = await syncTrackedBundlePullRequestIfNeeded(project, bundle, { ...options, rethrow: true });
1591
1599
  bundlePullRequestSyncErrorTimestamps.delete(key);
1592
- reconcileBundleTicketsAfterPullRequestSync(project.id, bundle.id);
1600
+ emitBundlePullRequestUpdated(project.id, bundle.id);
1601
+ scheduleBundleTicketReconciliationIfTerminal(project.id, bundle.id, nextPullRequest?.state ?? null);
1593
1602
  }
1594
1603
  catch (error) {
1595
1604
  bundlePullRequestSyncErrorTimestamps.set(key, Date.now());
@@ -1666,6 +1675,26 @@ const reconcileBundleTicketsAfterPullRequestSync = (projectId, bundleId) => {
1666
1675
  }
1667
1676
  }
1668
1677
  };
1678
+ const emitBundlePullRequestUpdated = (projectId, bundleId) => {
1679
+ io.emit('bundle-pull-request:updated', {
1680
+ project_id: projectId,
1681
+ bundle_id: bundleId,
1682
+ pull_request: serializeBundlePullRequest(getBundlePullRequestRecord(bundleId, projectId)),
1683
+ });
1684
+ };
1685
+ const scheduleBundleTicketReconciliationIfTerminal = (projectId, bundleId, nextState) => {
1686
+ if (nextState !== 'merged' && nextState !== 'closed') {
1687
+ return;
1688
+ }
1689
+ setImmediate(() => {
1690
+ try {
1691
+ reconcileBundleTicketsAfterPullRequestSync(projectId, bundleId);
1692
+ }
1693
+ catch (error) {
1694
+ console.warn(`[integrations] github pr ticket reconciliation failed project=${projectId} bundle=${bundleId}`, error);
1695
+ }
1696
+ });
1697
+ };
1669
1698
  const syncProjectBundlePullRequestsIfNeeded = async (project, bundles, options = {}) => {
1670
1699
  let startedCount = 0;
1671
1700
  for (const bundle of bundles) {
@@ -1995,7 +2024,7 @@ const buildBundleTickets = (projectId, bundleId, broadcaster) => {
1995
2024
  value: details,
1996
2025
  };
1997
2026
  };
1998
- const getCurrentReviewWorkspaceSignature = (ticket) => {
2027
+ const getCurrentReviewWorkspaceSignature = async (ticket) => {
1999
2028
  if (!ticket) {
2000
2029
  return null;
2001
2030
  }
@@ -2003,9 +2032,9 @@ const getCurrentReviewWorkspaceSignature = (ticket) => {
2003
2032
  if ('error' in workspace) {
2004
2033
  return null;
2005
2034
  }
2006
- return getGitStatus(workspace.cwd, workspace.branch, workspace.project).workspace_signature ?? null;
2035
+ return (await getGitStatusAsync(workspace.cwd, workspace.branch, workspace.project)).workspace_signature ?? null;
2007
2036
  };
2008
- const getCurrentReviewDiffSignature = (ticket) => {
2037
+ const getCurrentReviewDiffSignature = async (ticket) => {
2009
2038
  if (!ticket) {
2010
2039
  return null;
2011
2040
  }
@@ -2013,9 +2042,9 @@ const getCurrentReviewDiffSignature = (ticket) => {
2013
2042
  if ('error' in workspace) {
2014
2043
  return null;
2015
2044
  }
2016
- return computeDiffSignature(getGitUnifiedDiff(workspace.cwd));
2045
+ return computeDiffSignature(await getGitUnifiedDiffAsync(workspace.cwd));
2017
2046
  };
2018
- const getBundleReviewFindings = (projectId, bundleId) => {
2047
+ const getBundleReviewFindings = async (projectId, bundleId) => {
2019
2048
  const bundle = getBundle(bundleId, projectId);
2020
2049
  if (!bundle) {
2021
2050
  return null;
@@ -2046,13 +2075,13 @@ const getBundleReviewFindings = (projectId, bundleId) => {
2046
2075
  return buildReviewFindingsResponse({
2047
2076
  context,
2048
2077
  isContextRunning: tickets.some(ticket => ticket.agent_status === 'running'),
2049
- currentWorkspaceSignature: getCurrentReviewWorkspaceSignature(representativeTicket),
2078
+ currentWorkspaceSignature: await getCurrentReviewWorkspaceSignature(representativeTicket),
2050
2079
  currentDiffSignature: latestReviewRun && latestReviewRun.workspace_signature === null
2051
- ? getCurrentReviewDiffSignature(representativeTicket)
2080
+ ? await getCurrentReviewDiffSignature(representativeTicket)
2052
2081
  : null,
2053
2082
  });
2054
2083
  };
2055
- const getTicketReviewFindings = (ticketId) => {
2084
+ const getTicketReviewFindings = async (ticketId) => {
2056
2085
  const ticket = getTicketRouteContext(ticketId);
2057
2086
  if (!ticket) {
2058
2087
  return null;
@@ -2066,9 +2095,9 @@ const getTicketReviewFindings = (ticketId) => {
2066
2095
  return buildReviewFindingsResponse({
2067
2096
  context,
2068
2097
  isContextRunning: ticket.agent_status === 'running',
2069
- currentWorkspaceSignature: getCurrentReviewWorkspaceSignature(ticket),
2098
+ currentWorkspaceSignature: await getCurrentReviewWorkspaceSignature(ticket),
2070
2099
  currentDiffSignature: latestReviewRun && latestReviewRun.workspace_signature === null
2071
- ? getCurrentReviewDiffSignature(ticket)
2100
+ ? await getCurrentReviewDiffSignature(ticket)
2072
2101
  : null,
2073
2102
  });
2074
2103
  };
@@ -2426,6 +2455,7 @@ const emitRunStateForContext = (ticket, isRunningValue) => {
2426
2455
  }
2427
2456
  };
2428
2457
  app.use(express.json({ limit: '5mb' }));
2458
+ registerRepositoryGraphRoutes(app, { emitter: io });
2429
2459
  const requireDesktopMobileAccessRequest = (req, res) => {
2430
2460
  const rejectionReason = validateDesktopMobileAccessRequest(req);
2431
2461
  if (!rejectionReason) {
@@ -2714,7 +2744,7 @@ app.post('/projects/:id/chat-sessions/:sessionId/stop', async (req, res) => {
2714
2744
  }
2715
2745
  return res.status(result.status).json(result.value);
2716
2746
  });
2717
- app.get('/projects/:id/chat-sessions/:sessionId/diff', (req, res) => {
2747
+ app.get('/projects/:id/chat-sessions/:sessionId/diff', async (req, res) => {
2718
2748
  const project = getProjectById(req.params.id);
2719
2749
  if (!project) {
2720
2750
  return res.status(404).json({ error: 'Not found' });
@@ -2727,14 +2757,14 @@ app.get('/projects/:id/chat-sessions/:sessionId/diff', (req, res) => {
2727
2757
  if (!workspace.target_available || !workspace.cwd) {
2728
2758
  return res.status(409).json({ error: workspace.target_unavailable_reason ?? 'Chat target is unavailable.' });
2729
2759
  }
2730
- const diffData = getGitDiffData(workspace.cwd);
2760
+ const diffData = await getGitDiffDataAsync(workspace.cwd);
2731
2761
  return res.json({
2732
2762
  stats: summarizeTicketDiffFiles(diffData.files),
2733
2763
  files: diffData.files,
2734
2764
  diff: diffData.diff,
2735
2765
  });
2736
2766
  });
2737
- app.get('/projects/:id/chat-sessions/:sessionId/git-status', (req, res) => {
2767
+ app.get('/projects/:id/chat-sessions/:sessionId/git-status', async (req, res) => {
2738
2768
  const project = getProjectById(req.params.id);
2739
2769
  if (!project) {
2740
2770
  return res.status(404).json({ error: 'Not found' });
@@ -2747,7 +2777,7 @@ app.get('/projects/:id/chat-sessions/:sessionId/git-status', (req, res) => {
2747
2777
  if (!workspace.target_available || !workspace.cwd) {
2748
2778
  return res.status(409).json({ error: workspace.target_unavailable_reason ?? 'Chat target is unavailable.' });
2749
2779
  }
2750
- return res.json(getGitStatus(workspace.cwd, workspace.branch, project));
2780
+ return res.json(await getGitStatusAsync(workspace.cwd, workspace.branch, project));
2751
2781
  });
2752
2782
  app.delete('/projects/:id/chat-sessions/:sessionId', (req, res) => {
2753
2783
  const project = getProjectById(req.params.id);
@@ -3328,14 +3358,14 @@ app.get('/tickets/:id', async (req, res) => {
3328
3358
  res.json(ticket);
3329
3359
  if (passivePullRequestContext) {
3330
3360
  const { project, bundle } = passivePullRequestContext;
3331
- startPassiveGitHubWork('ticket-open-pr-sync', () => {
3361
+ startPassiveGitHubWork(res, 'ticket-open-pr-sync', () => {
3332
3362
  startTrackedBundlePullRequestSyncIfNeeded(project, bundle);
3333
3363
  startBundlePullRequestDiscoveryIfNeeded(project, bundle);
3334
3364
  });
3335
3365
  }
3336
3366
  return;
3337
3367
  });
3338
- app.get('/tickets/:id/review-findings', (req, res) => {
3368
+ app.get('/tickets/:id/review-findings', async (req, res) => {
3339
3369
  const ticket = getTicketRouteContext(req.params.id);
3340
3370
  if (!ticket) {
3341
3371
  return res.status(404).json({ error: 'Not found' });
@@ -3343,7 +3373,7 @@ app.get('/tickets/:id/review-findings', (req, res) => {
3343
3373
  if (!matchesTicketProjectContext(ticket, req)) {
3344
3374
  return res.status(404).json({ error: 'Not found' });
3345
3375
  }
3346
- const response = getTicketReviewFindings(ticket.id);
3376
+ const response = await getTicketReviewFindings(ticket.id);
3347
3377
  if (!response) {
3348
3378
  return res.status(404).json({ error: 'Not found' });
3349
3379
  }
@@ -3359,7 +3389,7 @@ app.get('/tickets/:id/agent-runs', (req, res) => {
3359
3389
  }
3360
3390
  return res.json({ runs: listAgentRunsForParent('ticket', ticket.id) });
3361
3391
  });
3362
- app.get('/tickets/:id/diff', (req, res) => {
3392
+ app.get('/tickets/:id/diff', async (req, res) => {
3363
3393
  const ticket = getTicketRouteContext(req.params.id);
3364
3394
  if (!ticket) {
3365
3395
  return res.status(404).json({ error: 'Not found' });
@@ -3372,7 +3402,7 @@ app.get('/tickets/:id/diff', (req, res) => {
3372
3402
  const { status, error } = workspace;
3373
3403
  return res.status(status).json({ error });
3374
3404
  }
3375
- const diffData = getGitDiffData(workspace.cwd);
3405
+ const diffData = await getGitDiffDataAsync(workspace.cwd);
3376
3406
  return res.json({
3377
3407
  stats: summarizeTicketDiffFiles(diffData.files),
3378
3408
  files: diffData.files,
@@ -3393,7 +3423,7 @@ app.get('/tickets/:id/committed-diff', (req, res) => {
3393
3423
  }
3394
3424
  return sendUnpushedCommitDiff(res, workspace, getCommitHashQuery(req));
3395
3425
  });
3396
- app.get('/tickets/:id/git-status', (req, res) => {
3426
+ app.get('/tickets/:id/git-status', async (req, res) => {
3397
3427
  const ticket = getTicketRouteContext(req.params.id);
3398
3428
  if (!ticket) {
3399
3429
  return res.status(404).json({ error: 'Not found' });
@@ -3406,9 +3436,9 @@ app.get('/tickets/:id/git-status', (req, res) => {
3406
3436
  return res.status(workspace.status).json({ error: workspace.error });
3407
3437
  }
3408
3438
  const refreshRemote = req.query.refresh_remote === 'true';
3409
- return res.json(getGitStatus(workspace.cwd, workspace.branch, workspace.project, { refreshRemote }));
3439
+ return res.json(await getGitStatusAsync(workspace.cwd, workspace.branch, workspace.project, { refreshRemote }));
3410
3440
  });
3411
- app.get('/tickets/:id/git-commits', (req, res) => {
3441
+ app.get('/tickets/:id/git-commits', async (req, res) => {
3412
3442
  const ticket = getTicketRouteContext(req.params.id);
3413
3443
  if (!ticket) {
3414
3444
  return res.status(404).json({ error: 'Not found' });
@@ -3420,7 +3450,7 @@ app.get('/tickets/:id/git-commits', (req, res) => {
3420
3450
  if ('error' in workspace) {
3421
3451
  return res.status(workspace.status).json({ error: workspace.error });
3422
3452
  }
3423
- const summary = getUnpushedCommitSummaryForCwd(workspace.cwd, workspace.branch, workspace.project);
3453
+ const summary = await getUnpushedCommitSummaryForCwdAsync(workspace.cwd, workspace.branch, workspace.project);
3424
3454
  return res.json({ commits: summary.commits, total_count: summary.totalCount });
3425
3455
  });
3426
3456
  app.get('/bundles', async (req, res) => {
@@ -3440,7 +3470,7 @@ app.get('/bundles', async (req, res) => {
3440
3470
  timing.mark('serialize_bundles');
3441
3471
  timing.end({ count: serializedBundles.length });
3442
3472
  res.json(serializedBundles);
3443
- startPassiveGitHubWork('project-bundle-pr-sync', () => {
3473
+ startPassiveGitHubWork(res, 'project-bundle-pr-sync', () => {
3444
3474
  startProjectBundlePullRequestSyncIfNeeded(result.project, bundles);
3445
3475
  });
3446
3476
  });
@@ -3468,16 +3498,16 @@ app.get('/bundles/:id/conversation', (req, res) => {
3468
3498
  }
3469
3499
  const payload = serializeBundleConversationDetails(result.project, bundle);
3470
3500
  res.json(payload);
3471
- startPassiveGitHubWork('bundle-conversation-pr-discovery', () => {
3501
+ startPassiveGitHubWork(res, 'bundle-conversation-pr-discovery', () => {
3472
3502
  startBundlePullRequestDiscoveryIfNeeded(result.project, bundle);
3473
3503
  });
3474
3504
  });
3475
- app.get('/bundles/:id/review-findings', (req, res) => {
3505
+ app.get('/bundles/:id/review-findings', async (req, res) => {
3476
3506
  const result = getRequiredProject(req);
3477
3507
  if ('error' in result) {
3478
3508
  return res.status(400).json({ error: result.error });
3479
3509
  }
3480
- const response = getBundleReviewFindings(result.project.id, req.params.id);
3510
+ const response = await getBundleReviewFindings(result.project.id, req.params.id);
3481
3511
  if (!response) {
3482
3512
  return res.status(404).json({ error: 'Bundle not found' });
3483
3513
  }
@@ -3641,6 +3671,7 @@ app.post('/bundles/:id/pull-request', async (req, res) => {
3641
3671
  headBranch: bundle.branch,
3642
3672
  });
3643
3673
  upsertBundlePullRequestFromGitHub(result.project.id, bundle.id, githubConfig, nextPullRequest);
3674
+ emitBundlePullRequestUpdated(result.project.id, bundle.id);
3644
3675
  return res.json(serializeBundleDetails(result.project, bundle));
3645
3676
  }
3646
3677
  catch (error) {
@@ -3668,7 +3699,7 @@ app.post('/bundles/:id/pull-request/sync', async (req, res) => {
3668
3699
  if (!trackedPullRequest) {
3669
3700
  try {
3670
3701
  trackedPullRequest = await discoverOpenBundlePullRequest(result.project, bundle);
3671
- reconcileBundleTicketsAfterPullRequestSync(result.project.id, bundle.id);
3702
+ emitBundlePullRequestUpdated(result.project.id, bundle.id);
3672
3703
  return res.json(serializeBundleDetails(result.project, bundle));
3673
3704
  }
3674
3705
  catch (error) {
@@ -3681,8 +3712,9 @@ app.post('/bundles/:id/pull-request/sync', async (req, res) => {
3681
3712
  }
3682
3713
  try {
3683
3714
  const nextPullRequest = await getGitHubPullRequest(githubConfig, trackedPullRequest.number);
3684
- upsertBundlePullRequestFromGitHub(result.project.id, bundle.id, githubConfig, nextPullRequest);
3685
- reconcileBundleTicketsAfterPullRequestSync(result.project.id, bundle.id);
3715
+ const nextRecord = upsertBundlePullRequestFromGitHub(result.project.id, bundle.id, githubConfig, nextPullRequest);
3716
+ emitBundlePullRequestUpdated(result.project.id, bundle.id);
3717
+ scheduleBundleTicketReconciliationIfTerminal(result.project.id, bundle.id, nextRecord?.state ?? null);
3686
3718
  console.info(`[integrations] github pr sync completed project=${result.project.id} bundle=${bundle.id} pr=${trackedPullRequest.number}`);
3687
3719
  return res.json(serializeBundleDetails(result.project, bundle));
3688
3720
  }
@@ -3825,6 +3857,9 @@ app.delete('/bundles/:id', (req, res) => {
3825
3857
  if (hasActiveBundleSpecWork(result.project.id, bundle.id)) {
3826
3858
  return res.status(409).json({ error: 'Cannot delete a bundle while Spec work is active' });
3827
3859
  }
3860
+ if (hasActiveBundleRepositoryGraphScan(result.project.id, bundle.id)) {
3861
+ return res.status(409).json({ error: 'Cannot delete a bundle while a repository graph scan is active' });
3862
+ }
3828
3863
  if (countBundleTickets(bundle.id, result.project.id) > 0) {
3829
3864
  return res.status(409).json({ error: 'Cannot delete a bundle that is still used by tickets' });
3830
3865
  }
@@ -6843,7 +6878,7 @@ app.post('/projects/:id/git-abort', async (req, res) => {
6843
6878
  return res.status(500).json({ error: 'Unable to determine project branch' });
6844
6879
  }
6845
6880
  });
6846
- app.get('/projects/:id/diff', (req, res) => {
6881
+ app.get('/projects/:id/diff', async (req, res) => {
6847
6882
  const project = getProjectById(req.params.id);
6848
6883
  if (!project) {
6849
6884
  return res.status(404).json({ error: 'Not found' });
@@ -6852,7 +6887,7 @@ app.get('/projects/:id/diff', (req, res) => {
6852
6887
  if ('error' in workspace) {
6853
6888
  return res.status(workspace.status).json({ error: workspace.error });
6854
6889
  }
6855
- const diffData = getGitDiffData(workspace.cwd);
6890
+ const diffData = await getGitDiffDataAsync(workspace.cwd);
6856
6891
  return res.json({
6857
6892
  stats: summarizeTicketDiffFiles(diffData.files),
6858
6893
  files: diffData.files,
@@ -6870,7 +6905,7 @@ app.get('/projects/:id/committed-diff', (req, res) => {
6870
6905
  }
6871
6906
  return sendUnpushedCommitDiff(res, workspace, getCommitHashQuery(req));
6872
6907
  });
6873
- app.get('/projects/:id/git-status', (req, res) => {
6908
+ app.get('/projects/:id/git-status', async (req, res) => {
6874
6909
  const project = getProjectById(req.params.id);
6875
6910
  if (!project) {
6876
6911
  return res.status(404).json({ error: 'Not found' });
@@ -6880,9 +6915,9 @@ app.get('/projects/:id/git-status', (req, res) => {
6880
6915
  return res.status(workspace.status).json({ error: workspace.error });
6881
6916
  }
6882
6917
  const refreshRemote = req.query.refresh_remote === 'true';
6883
- return res.json(getGitStatus(workspace.cwd, workspace.branch, workspace.project, { refreshRemote }));
6918
+ return res.json(await getGitStatusAsync(workspace.cwd, workspace.branch, workspace.project, { refreshRemote }));
6884
6919
  });
6885
- app.get('/projects/:id/git-commits', (req, res) => {
6920
+ app.get('/projects/:id/git-commits', async (req, res) => {
6886
6921
  const project = getProjectById(req.params.id);
6887
6922
  if (!project) {
6888
6923
  return res.status(404).json({ error: 'Not found' });
@@ -6891,7 +6926,7 @@ app.get('/projects/:id/git-commits', (req, res) => {
6891
6926
  if ('error' in workspace) {
6892
6927
  return res.status(workspace.status).json({ error: workspace.error });
6893
6928
  }
6894
- const summary = getUnpushedCommitSummaryForCwd(workspace.cwd, workspace.branch, workspace.project);
6929
+ const summary = await getUnpushedCommitSummaryForCwdAsync(workspace.cwd, workspace.branch, workspace.project);
6895
6930
  return res.json({ commits: summary.commits, total_count: summary.totalCount });
6896
6931
  });
6897
6932
  app.post('/projects/:id/git-commit', async (req, res) => {
@@ -7248,6 +7283,9 @@ const assertProjectDeletionEligible = (projectId, claimId) => {
7248
7283
  if (hasActiveProjectCheckBatch(projectId)) {
7249
7284
  throw new ProjectDeletionError(409, 'Cannot delete a project while checks are active');
7250
7285
  }
7286
+ if (!claimId && hasActiveProjectRepositoryGraphScan(projectId)) {
7287
+ throw new ProjectDeletionError(409, 'Cannot delete a project while a repository graph scan is active');
7288
+ }
7251
7289
  };
7252
7290
  const completeClaimedProjectDeletion = async (projectId, claimId) => {
7253
7291
  await runExclusiveExecutionArtifactMaintenance(() => reconcileProjectExecutionArtifacts(projectId, {
@@ -9082,6 +9120,15 @@ const runExecutionEvidenceCleanup = () => {
9082
9120
  catch (error) {
9083
9121
  console.error('Failed to clean up expired execution evidence', error);
9084
9122
  }
9123
+ try {
9124
+ const result = pruneAllRepositoryGraphScans();
9125
+ if (result.total > 0) {
9126
+ console.log(`Pruned ${result.total} repository graph scan(s): ${result.completed} completed, ${result.non_completed} non-completed`);
9127
+ }
9128
+ }
9129
+ catch (error) {
9130
+ console.error('Failed to prune repository graph scans', error);
9131
+ }
9085
9132
  };
9086
9133
  const startExecutionEvidenceCleanupTimer = () => {
9087
9134
  if (executionEvidenceCleanupTimer) {
@@ -9155,6 +9202,13 @@ export const startServer = ({ port = DEFAULT_PORT, host } = {}) => {
9155
9202
  releaseInterruptedProjectDeletionClaims();
9156
9203
  await resumeInterruptedProjectDeletions();
9157
9204
  await reconcileStartupExecutions({ getGitHubConfig: getGitHubConnectionConfig });
9205
+ const repositoryGraphSchemaExists = db.prepare(`
9206
+ SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'repository_graph_scans'
9207
+ `).get();
9208
+ if (repositoryGraphSchemaExists) {
9209
+ const graphScans = await import('./lib/repositoryGraphScan.js');
9210
+ graphScans.openRepositoryGraphScanAdmissions();
9211
+ }
9158
9212
  throwIfShutdownRequested();
9159
9213
  await runExecutionArtifactCleanup('startup');
9160
9214
  throwIfShutdownRequested();
@@ -9215,6 +9269,12 @@ export const shutdownServer = async (signal) => {
9215
9269
  }
9216
9270
  await executionArtifactCleanupPromise;
9217
9271
  await stopMobileAccess();
9272
+ const repositoryGraphSchemaExists = db.prepare(`
9273
+ SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'repository_graph_scans'
9274
+ `).get();
9275
+ if (repositoryGraphSchemaExists) {
9276
+ await (await import('./lib/repositoryGraphScan.js')).shutdownRepositoryGraphScans();
9277
+ }
9218
9278
  await shutdownCheckRunner();
9219
9279
  await clearAllTestSessions(io);
9220
9280
  await clearAllGitHubActionsSessions();