@straiffi/archon 1.8.4 → 1.8.6

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 (96) hide show
  1. package/dist/client/assets/{ActivityView-CZwuxu-k.js → ActivityView-DDeEQgbm.js} +1 -1
  2. package/dist/client/assets/{GitHubActionsDialog-m0WCUbsY.js → GitHubActionsDialog-DH-B_LR3.js} +1 -1
  3. package/dist/client/assets/{VerificationDialog-D0LL2bcG.js → VerificationDialog-x43F7tbo.js} +1 -1
  4. package/dist/client/assets/{architectureDiagram-3BPJPVTR-Da0b3yts.js → architectureDiagram-3BPJPVTR-BH5LLK54.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-GPEHLZMM-DvzsxBUN.js → blockDiagram-GPEHLZMM-xOxShPwZ.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AAUBKEIU-BEKrFtx2.js → c4Diagram-AAUBKEIU-BXMrXnKR.js} +1 -1
  7. package/dist/client/assets/channel-C0dk5Jiy.js +1 -0
  8. package/dist/client/assets/{chunk-2J33WTMH-BAKgBD7I.js → chunk-2J33WTMH-BdDEtNMz.js} +1 -1
  9. package/dist/client/assets/{chunk-3OPIFGDE-BKPTlfa5.js → chunk-3OPIFGDE-PtSkTtIU.js} +1 -1
  10. package/dist/client/assets/{chunk-5ZQYHXKU-BDqDj4QJ.js → chunk-5ZQYHXKU-D5PYP24m.js} +1 -1
  11. package/dist/client/assets/{chunk-727SXJPM-Cs2r0JtD.js → chunk-727SXJPM-gDX0xZh1.js} +1 -1
  12. package/dist/client/assets/{chunk-AQP2D5EJ-DQbaJPN-.js → chunk-AQP2D5EJ-Bat82lQQ.js} +1 -1
  13. package/dist/client/assets/{chunk-CSCIHK7Q-CxuNtNrM.js → chunk-CSCIHK7Q-D3sau006.js} +1 -1
  14. package/dist/client/assets/{chunk-KSCS5N6A-Cw9Q8JPi.js → chunk-KSCS5N6A-CqEcB7Dr.js} +1 -1
  15. package/dist/client/assets/{chunk-L5ZTLDWV-CtuZjEe8.js → chunk-L5ZTLDWV-TRTuwXsF.js} +1 -1
  16. package/dist/client/assets/{chunk-LZXEDZCA-BQQddDdh.js → chunk-LZXEDZCA-DdpNmv37.js} +2 -2
  17. package/dist/client/assets/{chunk-ND2GUHAM-AK6DBTJe.js → chunk-ND2GUHAM-BCUL9zWr.js} +1 -1
  18. package/dist/client/assets/{chunk-NZK2D7GU-CWRA29FQ.js → chunk-NZK2D7GU-BAxuwE_2.js} +1 -1
  19. package/dist/client/assets/{chunk-O5CBEL6O-X-Oio9FM.js → chunk-O5CBEL6O-Cttpc9yf.js} +1 -1
  20. package/dist/client/assets/{chunk-WU5MYG2G-CETO3v_f.js → chunk-WU5MYG2G-620NtSlY.js} +1 -1
  21. package/dist/client/assets/classDiagram-4FO5ZUOK-C5ptkk7I.js +1 -0
  22. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BFuXUVOx.js +1 -0
  23. package/dist/client/assets/{dagre-BM42HDAG-Dt0-rccy.js → dagre-BM42HDAG-BTsZ2dQI.js} +1 -1
  24. package/dist/client/assets/{diagram-2AECGRRQ-BWRTEjaU.js → diagram-2AECGRRQ-afPu4kp4.js} +1 -1
  25. package/dist/client/assets/{diagram-5GNKFQAL-C2H5QcoG.js → diagram-5GNKFQAL-SixbJGPN.js} +1 -1
  26. package/dist/client/assets/{diagram-KO2AKTUF-RBK3VSKM.js → diagram-KO2AKTUF-Dk1670ag.js} +1 -1
  27. package/dist/client/assets/{diagram-LMA3HP47-RGh2ZH4s.js → diagram-LMA3HP47-BDNhjRaO.js} +1 -1
  28. package/dist/client/assets/{diagram-OG6HWLK6-DRZPfcn0.js → diagram-OG6HWLK6-BLowQCLY.js} +1 -1
  29. package/dist/client/assets/{erDiagram-TEJ5UH35-BMjeuzHP.js → erDiagram-TEJ5UH35-DAg9kUbm.js} +1 -1
  30. package/dist/client/assets/{flowDiagram-I6XJVG4X-DoLSPv4x.js → flowDiagram-I6XJVG4X-BsaPmiIS.js} +1 -1
  31. package/dist/client/assets/{ganttDiagram-6RSMTGT7-By8_6nMe.js → ganttDiagram-6RSMTGT7-DfF1TT-s.js} +1 -1
  32. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-2rHM4CiD.js → gitGraphDiagram-PVQCEYII-CEvTiN6n.js} +1 -1
  33. package/dist/client/assets/index-B5TSPCJr.css +2 -0
  34. package/dist/client/assets/index-DV0vl670.js +274 -0
  35. package/dist/client/assets/{infoDiagram-5YYISTIA-DWOg8hUJ.js → infoDiagram-5YYISTIA-CBjYYrzJ.js} +1 -1
  36. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-C9YznvSm.js → ishikawaDiagram-YF4QCWOH-BuCkp1uL.js} +1 -1
  37. package/dist/client/assets/{journeyDiagram-JHISSGLW-DSQDDTNI.js → journeyDiagram-JHISSGLW-DkUPCxT5.js} +1 -1
  38. package/dist/client/assets/{kanban-definition-UN3LZRKU-BkmCufHa.js → kanban-definition-UN3LZRKU-CXwNZ0Fa.js} +1 -1
  39. package/dist/client/assets/{line-cufAthH2.js → line-CRTZgJXA.js} +1 -1
  40. package/dist/client/assets/{mermaid-parser.core-BzbhogG7.js → mermaid-parser.core-NTByLn30.js} +1 -1
  41. package/dist/client/assets/{mermaid.core-CsQKe9Kh.js → mermaid.core-DszsB_Gl.js} +3 -3
  42. package/dist/client/assets/{mindmap-definition-RKZ34NQL-Ci6JrMb6.js → mindmap-definition-RKZ34NQL-DCrqCcPX.js} +1 -1
  43. package/dist/client/assets/{pieDiagram-4H26LBE5-CU9I_3Q_.js → pieDiagram-4H26LBE5-CbHeTKto.js} +1 -1
  44. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-DytyOGrA.js → quadrantDiagram-W4KKPZXB-Ptpkh0Dc.js} +1 -1
  45. package/dist/client/assets/{requirementDiagram-4Y6WPE33-CnrtyW9-.js → requirementDiagram-4Y6WPE33-73PM-Ae5.js} +1 -1
  46. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-CGAC1tbH.js → sankeyDiagram-5OEKKPKP-B7RCxzNy.js} +1 -1
  47. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-Cqlp3Fns.js → sequenceDiagram-3UESZ5HK-DGXfHCo0.js} +1 -1
  48. package/dist/client/assets/{stateDiagram-AJRCARHV-DspbWEGS.js → stateDiagram-AJRCARHV-Cl0_SVL3.js} +1 -1
  49. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CqzC1yAT.js +1 -0
  50. package/dist/client/assets/{timeline-definition-PNZ67QCA-Bd0SWRDD.js → timeline-definition-PNZ67QCA-Cufq-wYg.js} +1 -1
  51. package/dist/client/assets/{vennDiagram-CIIHVFJN-BZduwhis.js → vennDiagram-CIIHVFJN-ROVZhhP-.js} +1 -1
  52. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-D6LrBEnX.js → wardleyDiagram-YWT4CUSO-DlHRfLPH.js} +1 -1
  53. package/dist/client/assets/{xychartDiagram-2RQKCTM6-CoLKir5a.js → xychartDiagram-2RQKCTM6-DW-KpRbf.js} +1 -1
  54. package/dist/client/index.html +2 -2
  55. package/dist/server/index.js +118 -10
  56. package/dist/server/index.js.map +1 -1
  57. package/dist/server/lib/agent.js +28 -7
  58. package/dist/server/lib/agent.js.map +1 -1
  59. package/dist/server/lib/automationResumeDecision.js +6 -0
  60. package/dist/server/lib/automationResumeDecision.js.map +1 -1
  61. package/dist/server/lib/bundleReviewOperations.js +81 -2
  62. package/dist/server/lib/bundleReviewOperations.js.map +1 -1
  63. package/dist/server/lib/figmaResources.js +52 -0
  64. package/dist/server/lib/figmaResources.js.map +1 -0
  65. package/dist/server/lib/git.js +312 -32
  66. package/dist/server/lib/git.js.map +1 -1
  67. package/dist/server/lib/opencodeTaskSessions.js +19 -8
  68. package/dist/server/lib/opencodeTaskSessions.js.map +1 -1
  69. package/dist/server/lib/reviewFindings.js +31 -5
  70. package/dist/server/lib/reviewFindings.js.map +1 -1
  71. package/dist/server/lib/specArtifact.js +5 -1
  72. package/dist/server/lib/specArtifact.js.map +1 -1
  73. package/dist/server/lib/specResources.js +1 -4
  74. package/dist/server/lib/specResources.js.map +1 -1
  75. package/dist/server/lib/terminal.js +90 -30
  76. package/dist/server/lib/terminal.js.map +1 -1
  77. package/dist/server/lib/types.js.map +1 -1
  78. package/dist/server/workers/build.js +3 -2
  79. package/dist/server/workers/build.js.map +1 -1
  80. package/dist/server/workers/chat.js +5 -1
  81. package/dist/server/workers/chat.js.map +1 -1
  82. package/dist/server/workers/followUp.js +5 -1
  83. package/dist/server/workers/followUp.js.map +1 -1
  84. package/dist/server/workers/plan.js +36 -14
  85. package/dist/server/workers/plan.js.map +1 -1
  86. package/dist/server/workers/review.js +72 -6
  87. package/dist/server/workers/review.js.map +1 -1
  88. package/dist/server/workers/specSession.js +5 -2
  89. package/dist/server/workers/specSession.js.map +1 -1
  90. package/package.json +1 -1
  91. package/dist/client/assets/channel-CHuNNrYD.js +0 -1
  92. package/dist/client/assets/classDiagram-4FO5ZUOK-BZQ7jv-A.js +0 -1
  93. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-7EFkByjs.js +0 -1
  94. package/dist/client/assets/index-3dSPyX_Q.js +0 -274
  95. package/dist/client/assets/index-DZqqZx53.css +0 -2
  96. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Bcxu99EH.js +0 -1
@@ -19,7 +19,7 @@ import { resolveSessionTargetForExecution } from './lib/sessionTargets.js';
19
19
  import { createChatMessage, getChatMessage, updateChatMessageContent } from './lib/chatMessages.js';
20
20
  import { attachChatTicketProposalsToMessageContent } from './lib/chatTicketProposals.js';
21
21
  import { createCorsOriginResolver, parseCorsOrigins } from './lib/cors.js';
22
- import { abortGitOperation, branchExistsLocally, commitGitChanges, createWorktreeAsync, deleteWorktree, getBundleReviewDiffForCwd, getGitDiffData, getGitDiffFiles, getGitStatus, getGitUnifiedDiff, getLocalProjectBranch, getProjectBranch, getProjectBranchSuggestions, getSelectedGitUnifiedDiff, getUnpushedCommitDiffForCwd, hasKnownBranchRef, listUnpushedCommitsForCwd, pullGitBranch, pushCurrentBranch, rebaseGitBranchOntoBase, resolveExistingWorktreePath, resolveWorktreePath, summarizeTicketDiffFiles, switchProjectBranch, syncWorktreeFilesAsync, undoLatestUnpushedCommitForCwd, UnpushedCommitDiffSelectionError } from './lib/git.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';
23
23
  import config from './lib/config.js';
24
24
  import { autoConfigProject, ProjectAutoConfigError } from './lib/projectAutoConfig.js';
25
25
  import { analyzeRepoPath, createInitialCommit, ensureInitialCommitForWorktrees, initializeGitRepo, isInitialCommitRequiredError, } from './lib/projectRepos.js';
@@ -98,7 +98,8 @@ import { closePendingTicketPlanQuestionSet, listPendingTicketPlanQuestionSetMess
98
98
  import { hasPendingAskQuestionSet } from './lib/planQuestions.js';
99
99
  import { canUseBuildControls, formatActiveRunConflict, getTicketRunReadinessError, resumeBuildTicketRun, runProjectTarget, runTicketRun, stopBuildTicketRun, stopReviewTicketRun, stopProjectTargetRun, stopTicketRun, } from './lib/ticketRunOperations.js';
100
100
  import { isTicketAgentSettingsPayload, updateTicketAgentSettings, } from './lib/ticketSettingsOperations.js';
101
- import { rerunTicketReviewOperation, resolveBundleReviewOwner, transitionBundleToReview } from './lib/bundleReviewOperations.js';
101
+ import { getBundleReviewRecovery, recoverBundleReview, rerunTicketReviewOperation, resolveBundleReviewOwner, transitionBundleToReview } from './lib/bundleReviewOperations.js';
102
+ import { resolveReviewContinuation } from './workers/review.js';
102
103
  import { clearTicketStageSnapshots, getActiveBundleStageChangeHead, reconcileStageSnapshots, undoTicketStage } from './lib/ticketUndo.js';
103
104
  import { closeAllTerminalSessions, createProjectTerminalSession, createTerminalSessionForWorkspace, destroyTerminalSessionById, registerTerminalSocketHandlers, } from './lib/terminal.js';
104
105
  import { countBundleTickets, createTicketRecord, createBundle, deleteBundle, ensureProjectRootBundle, autoParkTicketIfStale, autoParkSettledBundleTicket, isBundleSettledForAutoPark, clearAutoParkDismissalIfNeeded, assignTicketDoneOrder, assignTicketLaneOrder, getBundle, getBundleByName, getBundleByBranch, getBundleRepresentativeTicketContext, isProjectRootBundle, getTicket, isBundledTicket, listTicketsInRunContext, listBundles, listBoardTicketEnrichment, listTickets, resolveTicketBranch, resolveTicketTool, updateBundleAutomationConfig, updateBundleIdentity, } from './lib/tickets.js';
@@ -1738,6 +1739,9 @@ const serializeBundleDetails = (project, bundle, extras = {}) => {
1738
1739
  has_completed_review: bundleReviewState.has_completed_review,
1739
1740
  has_follow_up_since_last_review: bundleReviewState.has_follow_up_since_last_review,
1740
1741
  can_re_review: bundleReviewState.can_re_review,
1742
+ review_recovery_available: getBundleAutomationState(project.id, bundle.id)?.automation_paused_reason
1743
+ ? false
1744
+ : getBundleReviewRecovery(project.id, bundle.id) !== null,
1741
1745
  has_helper_model: Boolean(project.helper_model),
1742
1746
  recent_commit_subjects: getBundleRecentCommitSubjects(project, bundle),
1743
1747
  };
@@ -3103,13 +3107,17 @@ app.post('/projects/:id/spec-sessions/:sessionId/snapshots/:snapshotId/create-ti
3103
3107
  }
3104
3108
  const target = targetResult.target;
3105
3109
  const bundleName = specSession.external_source?.external_key ?? '';
3106
- const bundle = bundleName
3107
- ? createAutoBundleForName(bundleName, project.id, target.branch)
3108
- : createAutoPlanningBundle(project.id, target.branch);
3110
+ const bundle = target.target_kind === 'bundle'
3111
+ ? target.target_bundle_id ? getBundle(target.target_bundle_id, project.id) : null
3112
+ : bundleName
3113
+ ? createAutoBundleForName(bundleName, project.id, target.branch)
3114
+ : createAutoPlanningBundle(project.id, target.branch);
3109
3115
  if (!bundle || isProjectRootBundle(bundle)) {
3110
3116
  throw new HttpError(409, 'Unable to create a bundle for this Spec snapshot.');
3111
3117
  }
3112
- generatedBundleId = bundle.id;
3118
+ if (target.target_kind === 'repo_root') {
3119
+ generatedBundleId = bundle.id;
3120
+ }
3113
3121
  const ticketsForMaterialization = snapshot.tickets.map(ticket => {
3114
3122
  const appendices = [
3115
3123
  buildVisualizationAppendix(ticket.relevant_visualizations ?? [], snapshot),
@@ -3412,7 +3420,8 @@ app.get('/tickets/:id/git-commits', (req, res) => {
3412
3420
  if ('error' in workspace) {
3413
3421
  return res.status(workspace.status).json({ error: workspace.error });
3414
3422
  }
3415
- return res.json({ commits: listUnpushedCommitsForCwd(workspace.cwd, workspace.branch, workspace.project) });
3423
+ const summary = getUnpushedCommitSummaryForCwd(workspace.cwd, workspace.branch, workspace.project);
3424
+ return res.json({ commits: summary.commits, total_count: summary.totalCount });
3416
3425
  });
3417
3426
  app.get('/bundles', async (req, res) => {
3418
3427
  const result = getRequiredProject(req);
@@ -4011,6 +4020,23 @@ app.post('/bundles/:id/review', (req, res) => {
4011
4020
  }
4012
4021
  return res.json(reviewResult.details);
4013
4022
  });
4023
+ app.post('/bundles/:id/review/continue', (req, res) => {
4024
+ const result = getRequiredProject(req);
4025
+ if ('error' in result) {
4026
+ return res.status(400).json({ error: result.error });
4027
+ }
4028
+ const bundle = getBundle(req.params.id, result.project.id);
4029
+ if (!bundle) {
4030
+ return res.status(404).json({ error: 'Bundle not found' });
4031
+ }
4032
+ const recovered = recoverBundleReview(result.project.id, bundle.id, io);
4033
+ if (!recovered.ok) {
4034
+ return res.status(recovered.status).json({ error: recovered.error });
4035
+ }
4036
+ return res.status(recovered.status).json(serializeBundleDetails(result.project, bundle, {
4037
+ target_ticket_id: recovered.value?.id ?? null,
4038
+ }));
4039
+ });
4014
4040
  app.post('/bundles/:id/build-open-questions/resolve', async (req, res) => {
4015
4041
  const result = getRequiredProject(req);
4016
4042
  if ('error' in result) {
@@ -4698,6 +4724,24 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
4698
4724
  return res.status(409).json({ error: error instanceof Error ? error.message : String(error) });
4699
4725
  }
4700
4726
  }
4727
+ else if (decision.outcome === 'resume_review') {
4728
+ if (!revalidateDecision()) {
4729
+ return res.status(409).json({ error: 'Bundle automation state changed before Resume could start' });
4730
+ }
4731
+ const sourceRunId = bundleState.automation_paused_stage_run_id;
4732
+ const reviewResult = rerunTicketReviewOperation(owner, io, {
4733
+ automatic: true,
4734
+ initiation: {
4735
+ causalParentRunId: progressionRunId,
4736
+ retrySourceRunId: sourceRunId,
4737
+ },
4738
+ continuation: sourceRunId ? resolveReviewContinuation(sourceRunId) : undefined,
4739
+ accept: acceptResumedReview,
4740
+ });
4741
+ if (!reviewResult.ok) {
4742
+ return res.status(reviewResult.status).json({ error: reviewResult.error });
4743
+ }
4744
+ }
4701
4745
  else if (decision.outcome === 'rerun_review') {
4702
4746
  if (!revalidateDecision()) {
4703
4747
  return res.status(409).json({ error: 'Bundle automation state changed before Resume could start' });
@@ -6847,7 +6891,8 @@ app.get('/projects/:id/git-commits', (req, res) => {
6847
6891
  if ('error' in workspace) {
6848
6892
  return res.status(workspace.status).json({ error: workspace.error });
6849
6893
  }
6850
- return res.json({ commits: listUnpushedCommitsForCwd(workspace.cwd, workspace.branch, workspace.project) });
6894
+ const summary = getUnpushedCommitSummaryForCwd(workspace.cwd, workspace.branch, workspace.project);
6895
+ return res.json({ commits: summary.commits, total_count: summary.totalCount });
6851
6896
  });
6852
6897
  app.post('/projects/:id/git-commit', async (req, res) => {
6853
6898
  const project = getProjectById(req.params.id);
@@ -7059,10 +7104,39 @@ app.post('/projects/:id/git-undo-latest', async (req, res) => {
7059
7104
  catch (error) {
7060
7105
  return res.status(409).json({ error: getErrorMessage(error) || 'Unable to undo the latest local commit right now.' });
7061
7106
  }
7107
+ const summary = getUnpushedCommitSummaryForCwd(workspace.cwd, workspace.branch, workspace.project);
7062
7108
  return res.json({
7063
7109
  ticket: null,
7064
7110
  git_status: getGitStatus(workspace.cwd, workspace.branch, workspace.project),
7065
- commits: listUnpushedCommitsForCwd(workspace.cwd, workspace.branch, workspace.project),
7111
+ commits: summary.commits,
7112
+ total_count: summary.totalCount,
7113
+ });
7114
+ });
7115
+ app.post('/projects/:id/git-squash', async (req, res) => {
7116
+ const project = getProjectById(req.params.id);
7117
+ if (!project) {
7118
+ return res.status(404).json({ error: 'Not found' });
7119
+ }
7120
+ const workspace = resolveProjectTargetGitWorkspace(project);
7121
+ if ('error' in workspace) {
7122
+ return res.status(workspace.status).json({ error: workspace.error });
7123
+ }
7124
+ const message = typeof req.body?.message === 'string' ? req.body.message.trim() : '';
7125
+ if (!message) {
7126
+ return res.status(400).json({ error: 'message is required' });
7127
+ }
7128
+ try {
7129
+ await squashUnpushedCommitsForCwd(workspace.cwd, workspace.branch, workspace.project, message);
7130
+ }
7131
+ catch (error) {
7132
+ return res.status(409).json({ error: getErrorMessage(error) || 'Unable to squash local commits right now.' });
7133
+ }
7134
+ const summary = getUnpushedCommitSummaryForCwd(workspace.cwd, workspace.branch, workspace.project);
7135
+ return res.json({
7136
+ ticket: null,
7137
+ git_status: getGitStatus(workspace.cwd, workspace.branch, workspace.project),
7138
+ commits: summary.commits,
7139
+ total_count: summary.totalCount,
7066
7140
  });
7067
7141
  });
7068
7142
  app.patch('/projects/:id', (req, res) => {
@@ -8032,11 +8106,45 @@ app.post('/tickets/:id/git-undo-latest', async (req, res) => {
8032
8106
  catch (error) {
8033
8107
  return res.status(409).json({ error: getErrorMessage(error) || 'Unable to undo the latest local commit right now.' });
8034
8108
  }
8109
+ const summary = getUnpushedCommitSummaryForCwd(workspace.cwd, workspace.branch, workspace.project);
8110
+ const updated = reconcileRunContextTickets(previousTickets, ticket.id);
8111
+ return res.json({
8112
+ ticket: updated,
8113
+ git_status: getGitStatus(workspace.cwd, workspace.branch, workspace.project),
8114
+ commits: summary.commits,
8115
+ total_count: summary.totalCount,
8116
+ });
8117
+ });
8118
+ app.post('/tickets/:id/git-squash', async (req, res) => {
8119
+ const ticket = getTicket(req.params.id);
8120
+ if (!ticket) {
8121
+ return res.status(404).json({ error: 'Not found' });
8122
+ }
8123
+ if (!matchesTicketProjectContext(ticket, req)) {
8124
+ return res.status(404).json({ error: 'Not found' });
8125
+ }
8126
+ const workspace = resolveTicketGitWorkspace(ticket);
8127
+ if ('error' in workspace) {
8128
+ return res.status(workspace.status).json({ error: workspace.error });
8129
+ }
8130
+ const message = typeof req.body?.message === 'string' ? req.body.message.trim() : '';
8131
+ if (!message) {
8132
+ return res.status(400).json({ error: 'message is required' });
8133
+ }
8134
+ const previousTickets = listTicketsInRunContext(ticket);
8135
+ try {
8136
+ await squashUnpushedCommitsForCwd(workspace.cwd, workspace.branch, workspace.project, message);
8137
+ }
8138
+ catch (error) {
8139
+ return res.status(409).json({ error: getErrorMessage(error) || 'Unable to squash local commits right now.' });
8140
+ }
8141
+ const summary = getUnpushedCommitSummaryForCwd(workspace.cwd, workspace.branch, workspace.project);
8035
8142
  const updated = reconcileRunContextTickets(previousTickets, ticket.id);
8036
8143
  return res.json({
8037
8144
  ticket: updated,
8038
8145
  git_status: getGitStatus(workspace.cwd, workspace.branch, workspace.project),
8039
- commits: listUnpushedCommitsForCwd(workspace.cwd, workspace.branch, workspace.project),
8146
+ commits: summary.commits,
8147
+ total_count: summary.totalCount,
8040
8148
  });
8041
8149
  });
8042
8150
  app.post('/projects/:id/open-ide', (req, res) => {