@straiffi/archon 1.3.11 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/assets/{TestsDialog-BUCv8I-8.js → TestsDialog-CD_kQh8y.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-C3Jb8g9R.js → architectureDiagram-3BPJPVTR-eKhX8J5g.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-CZcqQgBX.js → blockDiagram-GPEHLZMM-dYzMDN2p.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-Cpv5hJF9.js → c4Diagram-AAUBKEIU-Bfe91lzp.js} +1 -1
- package/dist/client/assets/channel-ByZgAb3R.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-DkbZ544o.js → chunk-2J33WTMH-Box1ZnSe.js} +1 -1
- package/dist/client/assets/{chunk-3OPIFGDE-CTkBmjbl.js → chunk-3OPIFGDE-BOkhWfjl.js} +1 -1
- package/dist/client/assets/{chunk-5ZQYHXKU-Cvt23bYu.js → chunk-5ZQYHXKU-DZt1zqaf.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-DX5ql843.js → chunk-727SXJPM-DZYZCHLI.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-BWR4RAvi.js → chunk-AQP2D5EJ-CmgJLI9e.js} +1 -1
- package/dist/client/assets/{chunk-CSCIHK7Q-BrZJ9QMn.js → chunk-CSCIHK7Q-D8FHELNF.js} +1 -1
- package/dist/client/assets/{chunk-KSCS5N6A-Xsa2cnhz.js → chunk-KSCS5N6A-DtLUz0Po.js} +1 -1
- package/dist/client/assets/{chunk-L5ZTLDWV-5L-9s_-b.js → chunk-L5ZTLDWV-CBk5m6ON.js} +1 -1
- package/dist/client/assets/{chunk-LZXEDZCA-XcqcrPu3.js → chunk-LZXEDZCA-CaexlXfB.js} +2 -2
- package/dist/client/assets/{chunk-ND2GUHAM-Cehi3-t2.js → chunk-ND2GUHAM-ma7eMIEB.js} +1 -1
- package/dist/client/assets/{chunk-NZK2D7GU-s6NmMa8K.js → chunk-NZK2D7GU-Da9F4EmF.js} +1 -1
- package/dist/client/assets/{chunk-O5CBEL6O-CkU-v8ar.js → chunk-O5CBEL6O-BLScYRER.js} +1 -1
- package/dist/client/assets/{chunk-WU5MYG2G-DJrhJw82.js → chunk-WU5MYG2G-r_Zx_ykt.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-ALbA3AZT.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CRtCF2Am.js +1 -0
- package/dist/client/assets/{dagre-BM42HDAG-B-PypVRB.js → dagre-BM42HDAG-CQZZ6fkN.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-Cm3pGoMu.js → diagram-2AECGRRQ-8fz_9Wj4.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-DCwV7VX3.js → diagram-5GNKFQAL-D2QIzYwx.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-B5RVwHze.js → diagram-KO2AKTUF-Dg7-VWuS.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-CmLaK5_k.js → diagram-LMA3HP47-CyvkOeSI.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-Mj0hhakE.js → diagram-OG6HWLK6-C6TmKjxR.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-D30rUvl5.js → erDiagram-TEJ5UH35-KO_Kcloa.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-Dh1RDWX7.js → flowDiagram-I6XJVG4X-BYDZ3qQE.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-B7f77DA_.js → ganttDiagram-6RSMTGT7-BLDujCf9.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CKBXqaKU.js → gitGraphDiagram-PVQCEYII-B-zvAAKD.js} +1 -1
- package/dist/client/assets/{index-Cs7XmROA.js → index-C5vJR8lI.js} +114 -114
- package/dist/client/assets/index-mgNREnGE.css +2 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-BPpU75Ej.js → infoDiagram-5YYISTIA-BZadMEs3.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-C4J2Eb6K.js → ishikawaDiagram-YF4QCWOH-BnuStPyf.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-BthEH9FE.js → journeyDiagram-JHISSGLW-13FrVYMv.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-DzSlAH8r.js → kanban-definition-UN3LZRKU-B4yGkr5P.js} +1 -1
- package/dist/client/assets/{line-BTeJbdYR.js → line-D3D8vY5q.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-BJonz176.js → mermaid-parser.core-DE23MjEP.js} +1 -1
- package/dist/client/assets/{mermaid.core-tt3Uc0ap.js → mermaid.core-C47RYkRV.js} +3 -3
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-iITkeEX1.js → mindmap-definition-RKZ34NQL-H9vxHA6z.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5--CgS4G7C.js → pieDiagram-4H26LBE5-gtm2r2QV.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-Clq6DEX-.js → quadrantDiagram-W4KKPZXB-CvQ8DgZT.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-r_apj3BK.js → requirementDiagram-4Y6WPE33-BJdKQQGp.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-2XqJHFyi.js → sankeyDiagram-5OEKKPKP-C8MvJ4qc.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-k4Qrdp_I.js → sequenceDiagram-3UESZ5HK-vnLK2sVC.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-CMr088KL.js → stateDiagram-AJRCARHV-xmrmmwh0.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-DCX_KXXR.js +1 -0
- package/dist/client/assets/{timeline-definition-PNZ67QCA-BpkNV3oH.js → timeline-definition-PNZ67QCA-BS6qgXzV.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-Bmte21GM.js → vennDiagram-CIIHVFJN-BY2_Ffta.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-CLJX-O9B.js → wardleyDiagram-YWT4CUSO-CkoA3xv4.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-Bc_SL4rS.js → xychartDiagram-2RQKCTM6-DbdiR5DY.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/db.js +22 -0
- package/dist/server/db.js.map +1 -1
- package/dist/server/index.js +115 -167
- package/dist/server/index.js.map +1 -1
- package/dist/server/lib/bundlePullRequests.js +47 -0
- package/dist/server/lib/bundlePullRequests.js.map +1 -1
- package/dist/server/lib/bundleReviewOperations.js +83 -0
- package/dist/server/lib/bundleReviewOperations.js.map +1 -0
- package/dist/server/lib/commitMessage.js +32 -0
- package/dist/server/lib/commitMessage.js.map +1 -0
- package/dist/server/lib/integrations/github.js +7 -0
- package/dist/server/lib/integrations/github.js.map +1 -1
- package/dist/server/lib/projects.js +44 -0
- package/dist/server/lib/projects.js.map +1 -1
- package/dist/server/lib/pullRequestContent.js +77 -0
- package/dist/server/lib/pullRequestContent.js.map +1 -0
- package/dist/server/lib/ticketAutomation.js +194 -0
- package/dist/server/lib/ticketAutomation.js.map +1 -0
- package/dist/server/lib/ticketAutomationCommit.js +188 -0
- package/dist/server/lib/ticketAutomationCommit.js.map +1 -0
- package/dist/server/lib/ticketSettingsOperations.js +35 -2
- package/dist/server/lib/ticketSettingsOperations.js.map +1 -1
- package/dist/server/lib/ticketWorkflowOperations.js +11 -0
- package/dist/server/lib/ticketWorkflowOperations.js.map +1 -1
- package/dist/server/lib/tickets.js +31 -1
- package/dist/server/lib/tickets.js.map +1 -1
- package/dist/server/workers/build.js +11 -0
- package/dist/server/workers/build.js.map +1 -1
- package/dist/server/workers/followUp.js +30 -3
- package/dist/server/workers/followUp.js.map +1 -1
- package/dist/server/workers/review.js +78 -4
- package/dist/server/workers/review.js.map +1 -1
- package/package.json +1 -1
- package/dist/client/assets/channel-DmMIqd-b.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-CDTuTjGy.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BhEXEoEw.js +0 -1
- package/dist/client/assets/index-ZyUvSLAF.css +0 -2
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-t7kpZS05.js +0 -1
package/dist/server/index.js
CHANGED
|
@@ -40,17 +40,20 @@ import { getProjectContextResponse, startProjectContextScan } from './lib/projec
|
|
|
40
40
|
import { getProjectFileSuggestions } from './lib/projectFileSuggestions.js';
|
|
41
41
|
import { updateProjectTargetSelection } from './lib/projectTargets.js';
|
|
42
42
|
import { exportProjectMemoryArchive, importProjectMemoryArchive } from './lib/projectMemoryTransfer.js';
|
|
43
|
-
import { buildReviewFindingsResponse, computeDiffSignature, getLatestReviewRunForContext } from './lib/reviewFindings.js';
|
|
43
|
+
import { buildReviewFindingsResponse, computeDiffSignature, getLatestReviewRunForContext, listReviewFindings } from './lib/reviewFindings.js';
|
|
44
|
+
import { clearAutomationPause, countBlockingFindings, depthAtLeast, getBundleAutomationState, resolveAutomationConfig, } from './lib/ticketAutomation.js';
|
|
45
|
+
import { runAutomationCommitChain } from './lib/ticketAutomationCommit.js';
|
|
44
46
|
import { getDiscoveredSkills, normalizeSkillNames } from './lib/skills.js';
|
|
45
47
|
import { clearAllPreviewProxies } from './lib/previewProxy.js';
|
|
46
48
|
import { shutdownRealtimeServer } from './lib/shutdown.js';
|
|
47
49
|
import { resolveClientBuildPaths, shouldServeClientApp, STATIC_CLIENT_DIRECTORY_OPTIONS, STATIC_CLIENT_INDEX_OPTIONS, } from './lib/staticClient.js';
|
|
48
50
|
import { deleteIntegrationConnection, getGitHubConnectionConfig, getJiraConnectionConfig, listIntegrationConnections, upsertIntegrationConnection, } from './lib/integrations/index.js';
|
|
49
|
-
import { createGitHubPullRequest, findOpenGitHubPullRequest, findOpenGitHubPullRequestByHeadBranch, getGitHubPullRequestComments, getGitHubPullRequestReviews, getGitHubPullRequest, GitHubApiError, hasGitHubRemoteBranch, resolveGitHubRepository, validateGitHubConnection, } from './lib/integrations/github.js';
|
|
51
|
+
import { createGitHubPullRequest, createOrFindGitHubPullRequest, findOpenGitHubPullRequest, findOpenGitHubPullRequestByHeadBranch, getGitHubPullRequestComments, getGitHubPullRequestReviews, getGitHubPullRequest, GitHubApiError, hasGitHubRemoteBranch, resolveGitHubRepository, validateGitHubConnection, } from './lib/integrations/github.js';
|
|
50
52
|
import { importJiraIssue, JiraApiError, validateJiraConnection } from './lib/integrations/jira.js';
|
|
51
53
|
import { importGitHubIssue } from './lib/integrations/github-issues.js';
|
|
52
54
|
import { PlanningReferenceError, resolveExternalPlanningDescription } from './lib/integrations/planning.js';
|
|
53
|
-
import {
|
|
55
|
+
import { buildCommitMessagePrompt, normalizeGeneratedCommitMessage } from './lib/commitMessage.js';
|
|
56
|
+
import { BUNDLE_PULL_REQUEST_SYNC_TTL_MS, getBundlePullRequestRecord, hasOpenBundlePullRequest, serializeBundlePullRequest, shouldSyncBundlePullRequest, syncTrackedBundlePullRequestIfNeeded, upsertBundlePullRequest, upsertBundlePullRequestFromGitHub, } from './lib/bundlePullRequests.js';
|
|
54
57
|
import { countInboundProjectLinks, getEffectiveProject, getProjectById, getProjectTicketStats, hasProjectRunIde, listProjects, replaceProjectLinks, serializeProject, updateProjectActiveTarget, validateProjectPayload, } from './lib/projects.js';
|
|
55
58
|
import { getActiveRunStatus, getActiveRunStatusForProject, getPreviewStatusForProject, hasRunConfig, isRunning, openIde, openTicketIdeInWorkspace, resolveProjectRunContextKey, runSetupCommandsInWorkspace, runTicketInWorkspace, stopAllTickets, stopTicket } from './lib/run.js';
|
|
56
59
|
import { clearAllTestSessions, createTestSession, deleteTestSession, discoverTestsForSession, doesTestFileExistForSession, findProjectTestCommand, getTestSession, isTestSelectionSupported, startTestSessionRun, stopTestSessionRun } from './lib/testSessions.js';
|
|
@@ -58,6 +61,7 @@ import { getBundleBuildBlockerResponse, shouldStartBundleBuildNow, transitionTic
|
|
|
58
61
|
import { submitPlanTicketFollowUp, submitTicketFollowUp, } from './lib/ticketFollowUpOperations.js';
|
|
59
62
|
import { canUseBuildControls, formatActiveRunConflict, getTicketRunReadinessError, resumeBuildTicketRun, runProjectTarget, runTicketRun, stopBuildTicketRun, stopProjectTargetRun, stopTicketRun, } from './lib/ticketRunOperations.js';
|
|
60
63
|
import { isTicketAgentSettingsPayload, updateTicketAgentSettings, } from './lib/ticketSettingsOperations.js';
|
|
64
|
+
import { resolveBundleReviewOwner, transitionBundleToReview } from './lib/bundleReviewOperations.js';
|
|
61
65
|
import { getActiveBundleStageChangeHead, undoTicketStage } from './lib/ticketUndo.js';
|
|
62
66
|
import { closeAllTerminalSessions, createProjectTerminalSession, createTerminalSessionForWorkspace, destroyTerminalSessionById, registerTerminalSocketHandlers, } from './lib/terminal.js';
|
|
63
67
|
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, } from './lib/tickets.js';
|
|
@@ -279,7 +283,6 @@ const resolveRequestedSkills = (value) => normalizeSkillNames(value);
|
|
|
279
283
|
const asTrimmedString = (value) => {
|
|
280
284
|
return typeof value === 'string' ? value.trim() : '';
|
|
281
285
|
};
|
|
282
|
-
const COMMIT_MESSAGE_DIFF_CHAR_LIMIT = 12_000;
|
|
283
286
|
const chatAttachmentUpload = multer({
|
|
284
287
|
storage: multer.memoryStorage(),
|
|
285
288
|
limits: {
|
|
@@ -316,36 +319,6 @@ const getSpecAttachmentUploadErrorMessage = (error) => {
|
|
|
316
319
|
}
|
|
317
320
|
return 'Unable to upload the selected images right now.';
|
|
318
321
|
};
|
|
319
|
-
const normalizeGeneratedCommitMessage = (value) => {
|
|
320
|
-
const firstLine = value
|
|
321
|
-
.replace(/\r/g, '\n')
|
|
322
|
-
.split('\n')
|
|
323
|
-
.map(line => line.trim())
|
|
324
|
-
.find(line => line.length > 0) ?? '';
|
|
325
|
-
return firstLine
|
|
326
|
-
.replace(/^[-*]\s+/, '')
|
|
327
|
-
.replace(/^[`"']+/, '')
|
|
328
|
-
.replace(/[`"']+$/, '')
|
|
329
|
-
.trim();
|
|
330
|
-
};
|
|
331
|
-
const buildCommitMessagePrompt = ({ commitMessageRules, selectedFiles, diff, }) => {
|
|
332
|
-
const diffExcerpt = diff.length > COMMIT_MESSAGE_DIFF_CHAR_LIMIT
|
|
333
|
-
? `${diff.slice(0, COMMIT_MESSAGE_DIFF_CHAR_LIMIT)}\n\n[diff truncated for speed]`
|
|
334
|
-
: diff;
|
|
335
|
-
return [
|
|
336
|
-
'You generate git commit subjects quickly from a selected diff.',
|
|
337
|
-
'Prefer speed and a strong best-effort summary over perfect accuracy.',
|
|
338
|
-
'Return only the commit subject as a single line. No quotes. No markdown. No explanation.',
|
|
339
|
-
'Keep it concise and under 72 characters when practical.',
|
|
340
|
-
commitMessageRules
|
|
341
|
-
? `Commit message rules:\n${commitMessageRules}`
|
|
342
|
-
: 'Commit message rules:\nUse a clear default git subject line.',
|
|
343
|
-
`Selected files:\n${selectedFiles.map(file => `- ${file}`).join('\n')}`,
|
|
344
|
-
diffExcerpt.trim().length > 0
|
|
345
|
-
? `Selected diff:\n${diffExcerpt}`
|
|
346
|
-
: 'Selected diff:\n[no diff excerpt available, infer from file names only]',
|
|
347
|
-
].join('\n\n');
|
|
348
|
-
};
|
|
349
322
|
const normalizeGeneratedPullRequestDescription = (value) => {
|
|
350
323
|
const normalized = value
|
|
351
324
|
.replace(/\r\n/g, '\n')
|
|
@@ -895,11 +868,6 @@ const listBundleConversationTickets = (projectId, bundleId) => {
|
|
|
895
868
|
}))
|
|
896
869
|
: [];
|
|
897
870
|
};
|
|
898
|
-
const moveBundleTicketToReview = (ticket) => {
|
|
899
|
-
const laneOrder = assignTicketLaneOrder({ ...ticket, state: 'review' });
|
|
900
|
-
db.prepare('UPDATE tickets SET state = ?, tool = ?, agent_status = NULL, streaming_response = NULL, lane_order = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run('review', resolveTicketTool(ticket, config.tool ?? 'opencode'), laneOrder ?? null, ticket.id);
|
|
901
|
-
return getTicket(ticket.id);
|
|
902
|
-
};
|
|
903
871
|
const restoreBundleReviewTicketToBuild = (ticket) => {
|
|
904
872
|
const laneOrder = assignTicketLaneOrder({ ...ticket, state: 'build' });
|
|
905
873
|
db.prepare('UPDATE tickets SET state = ?, agent_status = ?, streaming_response = NULL, lane_order = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run('build', 'done', laneOrder ?? null, ticket.id);
|
|
@@ -979,7 +947,6 @@ const getBundleReviewState = (projectId, tickets) => {
|
|
|
979
947
|
can_re_review: reviewState.can_re_review,
|
|
980
948
|
};
|
|
981
949
|
};
|
|
982
|
-
const BUNDLE_PULL_REQUEST_SYNC_TTL_MS = 60 * 1000;
|
|
983
950
|
const BUNDLE_PULL_REQUEST_DISCOVERY_MISS_COOLDOWN_MS = 10 * 60 * 1000;
|
|
984
951
|
const BUNDLE_PULL_REQUEST_DISCOVERY_ERROR_COOLDOWN_MS = 2 * 60 * 1000;
|
|
985
952
|
const BUNDLE_PULL_REQUEST_SYNC_ERROR_COOLDOWN_MS = 2 * 60 * 1000;
|
|
@@ -1086,33 +1053,6 @@ const serializeGitHubPullRequestSummary = (pullRequest) => {
|
|
|
1086
1053
|
last_synced_at: null,
|
|
1087
1054
|
};
|
|
1088
1055
|
};
|
|
1089
|
-
const createOrFindGitHubPullRequest = async (config, input) => {
|
|
1090
|
-
const existingPullRequest = await findOpenGitHubPullRequest(config, {
|
|
1091
|
-
headBranch: input.headBranch,
|
|
1092
|
-
baseBranch: input.baseBranch,
|
|
1093
|
-
});
|
|
1094
|
-
return existingPullRequest ?? await createGitHubPullRequest(config, input);
|
|
1095
|
-
};
|
|
1096
|
-
const upsertBundlePullRequestFromGitHub = (projectId, bundleId, config, pullRequest) => {
|
|
1097
|
-
return upsertBundlePullRequest({
|
|
1098
|
-
bundleId,
|
|
1099
|
-
projectId,
|
|
1100
|
-
repoHost: config.host,
|
|
1101
|
-
repoOwner: config.repo_owner,
|
|
1102
|
-
repoName: config.repo_name,
|
|
1103
|
-
number: pullRequest.number,
|
|
1104
|
-
url: pullRequest.url,
|
|
1105
|
-
title: pullRequest.title,
|
|
1106
|
-
baseBranch: pullRequest.baseBranch,
|
|
1107
|
-
headBranch: pullRequest.headBranch,
|
|
1108
|
-
state: pullRequest.state,
|
|
1109
|
-
isDraft: pullRequest.isDraft,
|
|
1110
|
-
mergedAt: pullRequest.mergedAt,
|
|
1111
|
-
closedAt: pullRequest.closedAt,
|
|
1112
|
-
githubNodeId: pullRequest.nodeId,
|
|
1113
|
-
lastSyncedAt: new Date().toISOString(),
|
|
1114
|
-
});
|
|
1115
|
-
};
|
|
1116
1056
|
const discoverOpenBundlePullRequest = async (project, bundle) => {
|
|
1117
1057
|
const githubConfig = getGitHubConnectionConfig(project.id);
|
|
1118
1058
|
const existingRecord = getBundlePullRequestRecord(bundle.id, project.id);
|
|
@@ -1129,30 +1069,6 @@ const discoverOpenBundlePullRequest = async (project, bundle) => {
|
|
|
1129
1069
|
console.info(`[integrations] github pr discovered project=${project.id} bundle=${bundle.id} pr=${discoveredPullRequest.number}`);
|
|
1130
1070
|
return upsertBundlePullRequestFromGitHub(project.id, bundle.id, githubConfig, discoveredPullRequest);
|
|
1131
1071
|
};
|
|
1132
|
-
const syncTrackedBundlePullRequestIfNeeded = async (project, bundle, options = {}) => {
|
|
1133
|
-
const force = options.force ?? false;
|
|
1134
|
-
const rethrow = options.rethrow ?? false;
|
|
1135
|
-
const githubConfig = getGitHubConnectionConfig(project.id);
|
|
1136
|
-
const existingRecord = getBundlePullRequestRecord(bundle.id, project.id);
|
|
1137
|
-
if (!githubConfig || !existingRecord) {
|
|
1138
|
-
return existingRecord;
|
|
1139
|
-
}
|
|
1140
|
-
if (!force && !shouldSyncBundlePullRequest(existingRecord, BUNDLE_PULL_REQUEST_SYNC_TTL_MS)) {
|
|
1141
|
-
return existingRecord;
|
|
1142
|
-
}
|
|
1143
|
-
console.info(`[integrations] github pr sync project=${project.id} bundle=${bundle.id} pr=${existingRecord.number}`);
|
|
1144
|
-
try {
|
|
1145
|
-
const nextPullRequest = await getGitHubPullRequest(githubConfig, existingRecord.number);
|
|
1146
|
-
return upsertBundlePullRequestFromGitHub(project.id, bundle.id, githubConfig, nextPullRequest);
|
|
1147
|
-
}
|
|
1148
|
-
catch (error) {
|
|
1149
|
-
console.warn(`[integrations] github pr sync failed project=${project.id} bundle=${bundle.id} pr=${existingRecord.number}`, error);
|
|
1150
|
-
if (rethrow) {
|
|
1151
|
-
throw error;
|
|
1152
|
-
}
|
|
1153
|
-
return existingRecord;
|
|
1154
|
-
}
|
|
1155
|
-
};
|
|
1156
1072
|
const shouldStartTrackedBundlePullRequestSync = (project, bundle, options = {}) => {
|
|
1157
1073
|
const force = options.force ?? false;
|
|
1158
1074
|
const existingRecord = getBundlePullRequestRecord(bundle.id, project.id);
|
|
@@ -1390,35 +1306,6 @@ const submitBundleFollowUpResponse = (projectId, bundleId, payload, broadcaster)
|
|
|
1390
1306
|
}),
|
|
1391
1307
|
};
|
|
1392
1308
|
};
|
|
1393
|
-
const resolveBundleReviewOwner = (projectId, bundleId) => {
|
|
1394
|
-
const tickets = listDetailedBundleTickets(projectId, bundleId);
|
|
1395
|
-
const candidate = tickets.find(ticket => {
|
|
1396
|
-
if (ticket.state !== 'review') {
|
|
1397
|
-
return false;
|
|
1398
|
-
}
|
|
1399
|
-
const latestReviewHead = getActiveBundleStageChangeHead(ticket, 'review');
|
|
1400
|
-
return latestReviewHead?.ticket_id === ticket.id;
|
|
1401
|
-
});
|
|
1402
|
-
if (candidate) {
|
|
1403
|
-
return candidate;
|
|
1404
|
-
}
|
|
1405
|
-
return tickets.find(ticket => ticket.state === 'review' && ticket.undo?.stage === 'review') ?? null;
|
|
1406
|
-
};
|
|
1407
|
-
const getBundleReviewEligibilityError = (projectId, bundleId) => {
|
|
1408
|
-
const buildTickets = listBundleSliceTickets(projectId, bundleId, 'build');
|
|
1409
|
-
if (buildTickets.length === 0) {
|
|
1410
|
-
const reviewTickets = listBundleSliceTickets(projectId, bundleId, 'review');
|
|
1411
|
-
if (reviewTickets.some(ticket => ticket.can_re_review)) {
|
|
1412
|
-
return null;
|
|
1413
|
-
}
|
|
1414
|
-
return 'No review tickets are available to re-run in this bundle';
|
|
1415
|
-
}
|
|
1416
|
-
const blockingTicket = buildTickets.find(ticket => ticket.agent_status !== 'done' && ticket.agent_status !== 'stopped');
|
|
1417
|
-
if (blockingTicket) {
|
|
1418
|
-
return 'All build tickets in the bundle must be done or stopped before starting review';
|
|
1419
|
-
}
|
|
1420
|
-
return null;
|
|
1421
|
-
};
|
|
1422
1309
|
const rerunTicketReview = (ticketId) => {
|
|
1423
1310
|
const ticket = getTicket(ticketId);
|
|
1424
1311
|
if (!ticket) {
|
|
@@ -1445,53 +1332,18 @@ const reviewBundleTickets = (projectId, bundleId) => {
|
|
|
1445
1332
|
if (!bundle) {
|
|
1446
1333
|
return { ok: false, status: 404, error: 'Bundle not found' };
|
|
1447
1334
|
}
|
|
1448
|
-
const
|
|
1449
|
-
if (
|
|
1450
|
-
return { ok: false, status:
|
|
1451
|
-
}
|
|
1452
|
-
const buildTickets = listBundleSliceTickets(projectId, bundle.id, 'build');
|
|
1453
|
-
if (buildTickets.length === 0) {
|
|
1454
|
-
const reviewOwnerSource = resolveBundleReviewOwner(projectId, bundle.id)
|
|
1455
|
-
?? listBundleSliceTickets(projectId, bundle.id, 'review').find(ticket => ticket.can_re_review)
|
|
1456
|
-
?? null;
|
|
1457
|
-
if (!reviewOwnerSource || !reviewOwnerSource.can_re_review) {
|
|
1458
|
-
return { ok: false, status: 409, error: 'No review tickets are available to re-run in this bundle' };
|
|
1459
|
-
}
|
|
1460
|
-
const reviewOwner = getTicket(reviewOwnerSource.id);
|
|
1461
|
-
if (!reviewOwner) {
|
|
1462
|
-
return { ok: false, status: 409, error: 'Unable to start review for this bundle' };
|
|
1463
|
-
}
|
|
1464
|
-
startReview(reviewOwner, io);
|
|
1465
|
-
return {
|
|
1466
|
-
ok: true,
|
|
1467
|
-
bundle,
|
|
1468
|
-
reviewOwner,
|
|
1469
|
-
details: serializeBundleDetails(project, bundle, {
|
|
1470
|
-
review_owner_ticket_id: reviewOwner.id,
|
|
1471
|
-
target_ticket_id: reviewOwner.id,
|
|
1472
|
-
}),
|
|
1473
|
-
};
|
|
1474
|
-
}
|
|
1475
|
-
const reviewOwnerSource = buildTickets[0];
|
|
1476
|
-
if (!reviewOwnerSource) {
|
|
1477
|
-
return { ok: false, status: 409, error: 'Unable to start review for this bundle' };
|
|
1478
|
-
}
|
|
1479
|
-
const movedTickets = buildTickets
|
|
1480
|
-
.map(moveBundleTicketToReview)
|
|
1481
|
-
.filter((ticket) => ticket !== null);
|
|
1482
|
-
emitUpdatedTickets(movedTickets.map(ticket => ticket.id));
|
|
1483
|
-
const reviewOwner = getTicket(reviewOwnerSource.id);
|
|
1484
|
-
if (!reviewOwner) {
|
|
1485
|
-
return { ok: false, status: 409, error: 'Unable to start review for this bundle' };
|
|
1335
|
+
const result = transitionBundleToReview(projectId, bundle.id, io);
|
|
1336
|
+
if (!result.ok) {
|
|
1337
|
+
return { ok: false, status: result.status, error: result.error };
|
|
1486
1338
|
}
|
|
1487
|
-
|
|
1339
|
+
const reviewOwner = result.value;
|
|
1488
1340
|
return {
|
|
1489
1341
|
ok: true,
|
|
1490
1342
|
bundle,
|
|
1491
1343
|
reviewOwner,
|
|
1492
1344
|
details: serializeBundleDetails(project, bundle, {
|
|
1493
|
-
review_owner_ticket_id: reviewOwner
|
|
1494
|
-
target_ticket_id: reviewOwner
|
|
1345
|
+
review_owner_ticket_id: reviewOwner?.id,
|
|
1346
|
+
target_ticket_id: reviewOwner?.id,
|
|
1495
1347
|
}),
|
|
1496
1348
|
};
|
|
1497
1349
|
};
|
|
@@ -3432,6 +3284,94 @@ app.post('/bundles/:id/follow-up', (req, res) => {
|
|
|
3432
3284
|
}
|
|
3433
3285
|
return res.status(response.status).json(response.value);
|
|
3434
3286
|
});
|
|
3287
|
+
app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
3288
|
+
const result = getRequiredProject(req);
|
|
3289
|
+
if ('error' in result) {
|
|
3290
|
+
return res.status(400).json({ error: result.error });
|
|
3291
|
+
}
|
|
3292
|
+
const bundle = getBundle(req.params.id, result.project.id);
|
|
3293
|
+
if (!bundle) {
|
|
3294
|
+
return res.status(404).json({ error: 'Bundle not found' });
|
|
3295
|
+
}
|
|
3296
|
+
const bundleState = getBundleAutomationState(result.project.id, bundle.id);
|
|
3297
|
+
if (!bundleState || !bundleState.automation_paused_reason) {
|
|
3298
|
+
return res.status(409).json({ error: 'Bundle automation is not paused' });
|
|
3299
|
+
}
|
|
3300
|
+
clearAutomationPause(result.project.id, bundle.id);
|
|
3301
|
+
const reviewOwnerSource = resolveBundleReviewOwner(result.project.id, bundle.id)
|
|
3302
|
+
?? listBundleSliceTickets(result.project.id, bundle.id, 'review')[0]
|
|
3303
|
+
?? null;
|
|
3304
|
+
if (!reviewOwnerSource) {
|
|
3305
|
+
return res.status(409).json({ error: 'No review ticket to resume from' });
|
|
3306
|
+
}
|
|
3307
|
+
const owner = getTicket(reviewOwnerSource.id);
|
|
3308
|
+
if (!owner) {
|
|
3309
|
+
return res.status(409).json({ error: 'No review ticket to resume from' });
|
|
3310
|
+
}
|
|
3311
|
+
const project = getEffectiveProject(owner);
|
|
3312
|
+
if (!project) {
|
|
3313
|
+
return res.status(400).json({ error: 'Ticket project no longer exists' });
|
|
3314
|
+
}
|
|
3315
|
+
const automationConfig = resolveAutomationConfig(owner, project);
|
|
3316
|
+
if (owner.state === 'review' && owner.agent_status === 'running') {
|
|
3317
|
+
return res.status(409).json({ error: 'Review already running' });
|
|
3318
|
+
}
|
|
3319
|
+
if (owner.state === 'review' && owner.agent_status !== 'running') {
|
|
3320
|
+
const latestRun = getLatestReviewRunForContext({
|
|
3321
|
+
ticketId: owner.id,
|
|
3322
|
+
projectId: owner.project_id,
|
|
3323
|
+
worktreeBundleId: owner.worktree_bundle_id,
|
|
3324
|
+
});
|
|
3325
|
+
const findings = latestRun ? listReviewFindings(latestRun.id) : [];
|
|
3326
|
+
const blockingCount = countBlockingFindings(findings, automationConfig);
|
|
3327
|
+
const iteration = bundleState.review_iteration_count ?? 0;
|
|
3328
|
+
if (owner.can_re_review
|
|
3329
|
+
&& automationConfig.reviewFixEnabled
|
|
3330
|
+
&& blockingCount > 0
|
|
3331
|
+
&& iteration < automationConfig.reviewMaxIterations) {
|
|
3332
|
+
startFollowUp(owner, io, `Fix the code review findings at or above "${automationConfig.reviewSeverity}" severity. Address each blocking finding directly and minimally. Do not introduce unrelated changes, reformatting, or refactors. Do not commit.`, 'build');
|
|
3333
|
+
}
|
|
3334
|
+
else if (depthAtLeast(automationConfig.depth, 'auto_commit')) {
|
|
3335
|
+
await runAutomationCommitChain(owner, project, owner.bundle ?? null, automationConfig, io);
|
|
3336
|
+
}
|
|
3337
|
+
else {
|
|
3338
|
+
startReview(owner, io);
|
|
3339
|
+
}
|
|
3340
|
+
}
|
|
3341
|
+
else if (owner.state === 'build' && owner.agent_status === 'done') {
|
|
3342
|
+
const reviewResult = reviewBundleTickets(result.project.id, bundle.id);
|
|
3343
|
+
if (!reviewResult.ok) {
|
|
3344
|
+
return res.status(reviewResult.status).json({ error: reviewResult.error });
|
|
3345
|
+
}
|
|
3346
|
+
}
|
|
3347
|
+
else {
|
|
3348
|
+
return res.status(409).json({ error: 'Ticket is not in a resumable state' });
|
|
3349
|
+
}
|
|
3350
|
+
const resumedOwner = getTicket(owner.id);
|
|
3351
|
+
if (resumedOwner) {
|
|
3352
|
+
io.emit('ticket:updated', resumedOwner);
|
|
3353
|
+
}
|
|
3354
|
+
return res.json(serializeBundleDetails(result.project, bundle));
|
|
3355
|
+
});
|
|
3356
|
+
app.post('/bundles/:id/dismiss-automation-pause', (req, res) => {
|
|
3357
|
+
const result = getRequiredProject(req);
|
|
3358
|
+
if ('error' in result) {
|
|
3359
|
+
return res.status(400).json({ error: result.error });
|
|
3360
|
+
}
|
|
3361
|
+
const bundle = getBundle(req.params.id, result.project.id);
|
|
3362
|
+
if (!bundle) {
|
|
3363
|
+
return res.status(404).json({ error: 'Bundle not found' });
|
|
3364
|
+
}
|
|
3365
|
+
const bundleState = getBundleAutomationState(result.project.id, bundle.id);
|
|
3366
|
+
if (!bundleState || !bundleState.automation_paused_reason) {
|
|
3367
|
+
return res.status(409).json({ error: 'Bundle automation is not paused' });
|
|
3368
|
+
}
|
|
3369
|
+
clearAutomationPause(result.project.id, bundle.id);
|
|
3370
|
+
for (const ticket of listDetailedBundleTickets(result.project.id, bundle.id)) {
|
|
3371
|
+
io.emit('ticket:updated', ticket);
|
|
3372
|
+
}
|
|
3373
|
+
return res.json(serializeBundleDetails(result.project, bundle));
|
|
3374
|
+
});
|
|
3435
3375
|
app.get('/tool', (_req, res) => {
|
|
3436
3376
|
res.json({ tool: config.tool ?? 'opencode' });
|
|
3437
3377
|
});
|
|
@@ -3920,7 +3860,7 @@ app.post('/projects', (req, res) => {
|
|
|
3920
3860
|
return res.status(400).json({ error: validation.error });
|
|
3921
3861
|
}
|
|
3922
3862
|
const projectCount = Number(db.prepare('SELECT COUNT(*) AS count FROM projects').get()?.count ?? 0);
|
|
3923
|
-
const { name, repo_path, linked_project_ids, run_setup, run_services, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, commit_message_rules, auto_park_stale_tickets, memory_enabled, worktree_sync, } = validation.values;
|
|
3863
|
+
const { name, repo_path, linked_project_ids, run_setup, run_services, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, commit_message_rules, auto_park_stale_tickets, memory_enabled, worktree_sync, automation_depth, automation_review_fix_enabled, automation_review_severity, automation_review_max_iterations, } = validation.values;
|
|
3924
3864
|
if (!repo_path) {
|
|
3925
3865
|
return res.status(400).json({ error: 'repo_path must be a non-empty string' });
|
|
3926
3866
|
}
|
|
@@ -3950,10 +3890,14 @@ app.post('/projects', (req, res) => {
|
|
|
3950
3890
|
commit_message_rules,
|
|
3951
3891
|
auto_park_stale_tickets,
|
|
3952
3892
|
memory_enabled,
|
|
3953
|
-
worktree_sync
|
|
3893
|
+
worktree_sync,
|
|
3894
|
+
automation_depth,
|
|
3895
|
+
automation_review_fix_enabled,
|
|
3896
|
+
automation_review_severity,
|
|
3897
|
+
automation_review_max_iterations
|
|
3954
3898
|
)
|
|
3955
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3956
|
-
`).run(id, name, normalizedRepoPath, run_setup, run_services, test_commands, run_ide ?? null, preview_service_name ?? null, preview_path ?? null, preview_capability_mode ?? null, helper_model ?? null, helper_variant ?? null, commit_message_rules ?? null, auto_park_stale_tickets ?? 0, memory_enabled ?? 0, worktree_sync ?? null);
|
|
3899
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3900
|
+
`).run(id, name, normalizedRepoPath, run_setup, run_services, test_commands, run_ide ?? null, preview_service_name ?? null, preview_path ?? null, preview_capability_mode ?? null, helper_model ?? null, helper_variant ?? null, commit_message_rules ?? null, auto_park_stale_tickets ?? 0, memory_enabled ?? 0, worktree_sync ?? null, automation_depth ?? 'manual', automation_review_fix_enabled ?? 0, automation_review_severity ?? 'medium', automation_review_max_iterations ?? 3);
|
|
3957
3901
|
replaceProjectLinks(id, linked_project_ids ?? []);
|
|
3958
3902
|
ensureProjectRootBundle(id);
|
|
3959
3903
|
if (projectCount === 0) {
|
|
@@ -4928,7 +4872,7 @@ app.patch('/projects/:id', (req, res) => {
|
|
|
4928
4872
|
});
|
|
4929
4873
|
}
|
|
4930
4874
|
}
|
|
4931
|
-
const { name, repo_path, linked_project_ids, run_setup, run_services, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, commit_message_rules, auto_park_stale_tickets, memory_enabled, worktree_sync, } = validation.values;
|
|
4875
|
+
const { name, repo_path, linked_project_ids, run_setup, run_services, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, commit_message_rules, auto_park_stale_tickets, memory_enabled, worktree_sync, automation_depth, automation_review_fix_enabled, automation_review_severity, automation_review_max_iterations, } = validation.values;
|
|
4932
4876
|
db.prepare(`
|
|
4933
4877
|
UPDATE projects SET
|
|
4934
4878
|
name = ?,
|
|
@@ -4946,9 +4890,13 @@ app.patch('/projects/:id', (req, res) => {
|
|
|
4946
4890
|
auto_park_stale_tickets = ?,
|
|
4947
4891
|
memory_enabled = ?,
|
|
4948
4892
|
worktree_sync = ?,
|
|
4893
|
+
automation_depth = ?,
|
|
4894
|
+
automation_review_fix_enabled = ?,
|
|
4895
|
+
automation_review_severity = ?,
|
|
4896
|
+
automation_review_max_iterations = ?,
|
|
4949
4897
|
updated_at = CURRENT_TIMESTAMP
|
|
4950
4898
|
WHERE id = ?
|
|
4951
|
-
`).run(name ?? project.name, repo_path ?? project.repo_path, hasOwn(validation.values, 'run_setup') ? run_setup : project.run_setup, hasOwn(validation.values, 'run_services') ? run_services : project.run_services, hasOwn(validation.values, 'test_commands') ? test_commands : project.test_commands, hasOwn(validation.values, 'run_ide') ? run_ide : project.run_ide, hasOwn(validation.values, 'preview_service_name') ? preview_service_name : project.preview_service_name, hasOwn(validation.values, 'preview_path') ? preview_path : project.preview_path, hasOwn(validation.values, 'preview_capability_mode') ? preview_capability_mode : project.preview_capability_mode, hasOwn(validation.values, 'helper_model') ? helper_model : project.helper_model, hasOwn(validation.values, 'helper_variant') ? helper_variant : project.helper_variant, hasOwn(validation.values, 'commit_message_rules') ? commit_message_rules : project.commit_message_rules, hasOwn(validation.values, 'auto_park_stale_tickets') ? auto_park_stale_tickets : project.auto_park_stale_tickets, hasOwn(validation.values, 'memory_enabled') ? memory_enabled : project.memory_enabled, hasOwn(validation.values, 'worktree_sync') ? worktree_sync : project.worktree_sync, req.params.id);
|
|
4899
|
+
`).run(name ?? project.name, repo_path ?? project.repo_path, hasOwn(validation.values, 'run_setup') ? run_setup : project.run_setup, hasOwn(validation.values, 'run_services') ? run_services : project.run_services, hasOwn(validation.values, 'test_commands') ? test_commands : project.test_commands, hasOwn(validation.values, 'run_ide') ? run_ide : project.run_ide, hasOwn(validation.values, 'preview_service_name') ? preview_service_name : project.preview_service_name, hasOwn(validation.values, 'preview_path') ? preview_path : project.preview_path, hasOwn(validation.values, 'preview_capability_mode') ? preview_capability_mode : project.preview_capability_mode, hasOwn(validation.values, 'helper_model') ? helper_model : project.helper_model, hasOwn(validation.values, 'helper_variant') ? helper_variant : project.helper_variant, hasOwn(validation.values, 'commit_message_rules') ? commit_message_rules : project.commit_message_rules, hasOwn(validation.values, 'auto_park_stale_tickets') ? auto_park_stale_tickets : project.auto_park_stale_tickets, hasOwn(validation.values, 'memory_enabled') ? memory_enabled : project.memory_enabled, hasOwn(validation.values, 'worktree_sync') ? worktree_sync : project.worktree_sync, hasOwn(validation.values, 'automation_depth') ? automation_depth : project.automation_depth, hasOwn(validation.values, 'automation_review_fix_enabled') ? automation_review_fix_enabled : project.automation_review_fix_enabled, hasOwn(validation.values, 'automation_review_severity') ? automation_review_severity : project.automation_review_severity, hasOwn(validation.values, 'automation_review_max_iterations') ? automation_review_max_iterations : project.automation_review_max_iterations, req.params.id);
|
|
4952
4900
|
if (hasOwn(validation.values, 'linked_project_ids')) {
|
|
4953
4901
|
replaceProjectLinks(project.id, linked_project_ids ?? []);
|
|
4954
4902
|
}
|