@straiffi/archon 1.6.1 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/assets/GitHubActionsDialog-B9_WJBOp.js +1 -0
- package/dist/client/assets/TestsDialog-DKvbjH_M.js +4 -0
- package/dist/client/assets/TicketChangesModal-C8rFoKjY.js +1 -0
- package/dist/client/assets/{arc-CnuHRbeq.js → arc-D2Nn9xIj.js} +1 -1
- package/dist/client/assets/architecture-7EHR7CIX-C-4Cp4ml.js +1 -0
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-cWqD9YRv.js → architectureDiagram-3BPJPVTR-DXRSRLL9.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-B8yyMr1c.js → blockDiagram-GPEHLZMM-D7EE6Dnv.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-B_yL8Tbe.js → c4Diagram-AAUBKEIU-UhVzFafr.js} +1 -1
- package/dist/client/assets/channel-CNoEXkwa.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-BF8hfYEU.js → chunk-2J33WTMH-obbfylVr.js} +1 -1
- package/dist/client/assets/{chunk-3OPIFGDE-BPDdyRDK.js → chunk-3OPIFGDE-DPVuS-uo.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-_WBpCe0m.js → chunk-4BX2VUAB-CtLS8D71.js} +1 -1
- package/dist/client/assets/{chunk-4EGX6M5U-BBQHxMeA.js → chunk-4EGX6M5U-Btnm0QNe.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-B-d-YzxI.js → chunk-55IACEB6-74CcJaub.js} +1 -1
- package/dist/client/assets/{chunk-5DO6E6H7-BA-oSVa-.js → chunk-5DO6E6H7-WLZ8L4ko.js} +1 -1
- package/dist/client/assets/{chunk-5ZQYHXKU-DEi4bnIa.js → chunk-5ZQYHXKU-Dsc03vy9.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-BTXHOTJi.js → chunk-727SXJPM-COE-iq_Z.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-D2TAJwxM.js → chunk-AQP2D5EJ-DnbAdr8m.js} +1 -1
- package/dist/client/assets/{chunk-BR22UD5L-prWX503j.js → chunk-BR22UD5L-Cou5beGe.js} +1 -1
- package/dist/client/assets/{chunk-BSJP7CBP-CNtf2uaW.js → chunk-BSJP7CBP-DJ57XcV5.js} +1 -1
- package/dist/client/assets/{chunk-CSCIHK7Q-Bdgg7p9T.js → chunk-CSCIHK7Q-DbTzC_WS.js} +2 -2
- package/dist/client/assets/{chunk-FHYWG6QK-BQ8n0J0S.js → chunk-FHYWG6QK-Ba10e2kj.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-CWQN4V4s.js → chunk-FMBD7UC4-CEo2wZMx.js} +1 -1
- package/dist/client/assets/{chunk-KSCS5N6A-Cey3fOI1.js → chunk-KSCS5N6A-xvviIayr.js} +1 -1
- package/dist/client/assets/{chunk-L5ZTLDWV-BSNGBgBH.js → chunk-L5ZTLDWV-DY4Q9Til.js} +1 -1
- package/dist/client/assets/{chunk-LZXEDZCA-B8qP34jM.js → chunk-LZXEDZCA-Bbeq2DZs.js} +2 -2
- package/dist/client/assets/{chunk-MPE355IW-gHN8AuU_.js → chunk-MPE355IW-MqEt-J8G.js} +1 -1
- package/dist/client/assets/{chunk-MZUSXYTE-Clzaz1kK.js → chunk-MZUSXYTE-DdNi9lhx.js} +1 -1
- package/dist/client/assets/{chunk-N66VUXT2-CkkAOkdt.js → chunk-N66VUXT2-CjemTW4E.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-COde7fTi.js → chunk-ND2GUHAM-CylX1p3K.js} +1 -1
- package/dist/client/assets/{chunk-NZK2D7GU-C62VVWrt.js → chunk-NZK2D7GU-BTcALpQq.js} +1 -1
- package/dist/client/assets/{chunk-O5CBEL6O-_20MzuKm.js → chunk-O5CBEL6O-Be_5dk2_.js} +1 -1
- package/dist/client/assets/{chunk-PUPMXCY4-DAdQFv8j.js → chunk-PUPMXCY4-ZvwTw0To.js} +1 -1
- package/dist/client/assets/chunk-QZHKN3VN-LI_RZca2.js +1 -0
- package/dist/client/assets/{chunk-UIBZB4QT-BRR84OYN.js → chunk-UIBZB4QT-BTY9be9s.js} +1 -1
- package/dist/client/assets/{chunk-WCWK7LTN-CQU7Fs_W.js → chunk-WCWK7LTN-Db1n3n3-.js} +1 -1
- package/dist/client/assets/chunk-WU5MYG2G-DXPsSM4Q.js +1 -0
- package/dist/client/assets/{chunk-XPW4576I-DafcxlUi.js → chunk-XPW4576I-DObFPxjB.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-BgmK8-LI.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Dj5LqBjo.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-DhJHP9hr.js → cose-bilkent-S5V4N54A-MlOPiG6P.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-jLUWHvr3.js → dagre-BM42HDAG-CABUW7xK.js} +1 -1
- package/dist/client/assets/{dagre-CdwzCXQr.js → dagre-BWNHuDdf.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-B6D2TKLb.js → diagram-2AECGRRQ-KvcNrpvf.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-bFJLLDWx.js → diagram-5GNKFQAL-DQV6-I4e.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-Bp01SL5f.js → diagram-KO2AKTUF-DYDk9roD.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-DwoeZFg9.js → diagram-LMA3HP47-Da-0u-HG.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-CjRd_tge.js → diagram-OG6HWLK6-CuD5eRBv.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-DzfIVd9j.js → erDiagram-TEJ5UH35-BL5dBE-b.js} +1 -1
- package/dist/client/assets/eventmodeling-FCH6USID-Dqb6QKoI.js +1 -0
- package/dist/client/assets/{flowDiagram-I6XJVG4X-Dq04zf9A.js → flowDiagram-I6XJVG4X-Bke5cWs7.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-D5f16YYP.js → ganttDiagram-6RSMTGT7-5OD-ayGn.js} +1 -1
- package/dist/client/assets/gitGraph-WXDBUCRP-DYEsqh93.js +1 -0
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CCD1CXBG.js → gitGraphDiagram-PVQCEYII-7Cf6FVfO.js} +1 -1
- package/dist/client/assets/index-D8EniXuq.css +2 -0
- package/dist/client/assets/index-DXuxAAzf.js +270 -0
- package/dist/client/assets/info-J43DQDTF-CfjXFZai.js +1 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-Bde4Z0o8.js → infoDiagram-5YYISTIA-BchMN9V-.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-CJ89ymGL.js → ishikawaDiagram-YF4QCWOH-D6TchjQy.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-Dw_sScmo.js → journeyDiagram-JHISSGLW-CwEcoeX-.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-DfWOcnxV.js → kanban-definition-UN3LZRKU-S2V9CLGR.js} +1 -1
- package/dist/client/assets/{line-CUQIbS7r.js → line-DRheChzQ.js} +1 -1
- package/dist/client/assets/{linear-DFIW-KnM.js → linear-DT4rSZvy.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-d287AjoM.js → mermaid-parser.core-CN13XjLe.js} +2 -2
- package/dist/client/assets/{mermaid.core-QqY2ZNMl.js → mermaid.core-JhdSpoF5.js} +3 -3
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-Bz3Yc70v.js → mindmap-definition-RKZ34NQL-657TKwRB.js} +1 -1
- package/dist/client/assets/{ordinal-C1FS1Sfe.js → ordinal-k--hYEme.js} +1 -1
- package/dist/client/assets/packet-YPE3B663-Dv0HkRHU.js +1 -0
- package/dist/client/assets/pie-LRSECV5Y-S5zPzZFN.js +1 -0
- package/dist/client/assets/{pieDiagram-4H26LBE5-BjMDHR6y.js → pieDiagram-4H26LBE5-DJ8eQ94X.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-BWDqf0X1.js → quadrantDiagram-W4KKPZXB-GpYFqyb1.js} +1 -1
- package/dist/client/assets/radar-GUYGQ44K-B7psLv22.js +1 -0
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-kspE4S60.js → requirementDiagram-4Y6WPE33-COZFWgq1.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-EuLhHbDR.js → sankeyDiagram-5OEKKPKP-BdCHyfcQ.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-BvXG3DJh.js → sequenceDiagram-3UESZ5HK-DAh3Ogx0.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-B-D0PYkH.js → stateDiagram-AJRCARHV-Ck8Yie8_.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-B6zibRqD.js +1 -0
- package/dist/client/assets/{timeline-definition-PNZ67QCA-CJnod6Jh.js → timeline-definition-PNZ67QCA-kYr9DNtP.js} +1 -1
- package/dist/client/assets/treeView-BLDUP644-B8H0kT67.js +1 -0
- package/dist/client/assets/treemap-LRROVOQU-DPLhFHnh.js +1 -0
- package/dist/client/assets/{vennDiagram-CIIHVFJN-CBh3tqZu.js → vennDiagram-CIIHVFJN-CC_SlRiC.js} +1 -1
- package/dist/client/assets/wardley-L42UT6IY-B5-Vb08V.js +1 -0
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-BBQOjYBd.js → wardleyDiagram-YWT4CUSO-CZ17ccSN.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-C8gU1LNj.js → xychartDiagram-2RQKCTM6-Blqaj-ci.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/db.js +133 -0
- package/dist/server/db.js.map +1 -1
- package/dist/server/index.js +299 -17
- package/dist/server/index.js.map +1 -1
- package/dist/server/lib/agent.js +12 -11
- package/dist/server/lib/agent.js.map +1 -1
- package/dist/server/lib/buildOpenQuestions.js +263 -0
- package/dist/server/lib/buildOpenQuestions.js.map +1 -0
- package/dist/server/lib/bundleReviewOperations.js +17 -3
- package/dist/server/lib/bundleReviewOperations.js.map +1 -1
- package/dist/server/lib/git.js +19 -3
- package/dist/server/lib/git.js.map +1 -1
- package/dist/server/lib/lightweightPrompt.js +2 -1
- package/dist/server/lib/lightweightPrompt.js.map +1 -1
- package/dist/server/lib/planModeQuestions.js +23 -1
- package/dist/server/lib/planModeQuestions.js.map +1 -1
- package/dist/server/lib/previewProxy.js +5 -0
- package/dist/server/lib/previewProxy.js.map +1 -1
- package/dist/server/lib/projectAutoConfig.js +2 -1
- package/dist/server/lib/projectAutoConfig.js.map +1 -1
- package/dist/server/lib/projectFiles.js +313 -0
- package/dist/server/lib/projectFiles.js.map +1 -0
- package/dist/server/lib/projects.js +10 -0
- package/dist/server/lib/projects.js.map +1 -1
- package/dist/server/lib/ticketAutomation.js +16 -4
- package/dist/server/lib/ticketAutomation.js.map +1 -1
- package/dist/server/lib/ticketAutomationCommit.js +10 -10
- package/dist/server/lib/ticketAutomationCommit.js.map +1 -1
- package/dist/server/lib/ticketOpenQuestions.js +17 -0
- package/dist/server/lib/ticketOpenQuestions.js.map +1 -1
- package/dist/server/lib/ticketWorkflowOperations.js +1 -1
- package/dist/server/lib/ticketWorkflowOperations.js.map +1 -1
- package/dist/server/lib/tickets.js +48 -6
- package/dist/server/lib/tickets.js.map +1 -1
- package/dist/server/workers/build.js +55 -12
- package/dist/server/workers/build.js.map +1 -1
- package/dist/server/workers/chat.js +2 -5
- package/dist/server/workers/chat.js.map +1 -1
- package/dist/server/workers/followUp.js +120 -18
- package/dist/server/workers/followUp.js.map +1 -1
- package/dist/server/workers/plan.js +4 -7
- package/dist/server/workers/plan.js.map +1 -1
- package/dist/server/workers/review.js +25 -8
- package/dist/server/workers/review.js.map +1 -1
- package/dist/server/workers/specSession.js +2 -5
- package/dist/server/workers/specSession.js.map +1 -1
- package/package.json +1 -1
- package/dist/client/assets/GitHubActionsDialog-BLJ3yCeK.js +0 -1
- package/dist/client/assets/TestsDialog-ar_RUzA3.js +0 -4
- package/dist/client/assets/architecture-7EHR7CIX-tSrokdFP.js +0 -1
- package/dist/client/assets/channel-1Ezn2R-x.js +0 -1
- package/dist/client/assets/chunk-QZHKN3VN-biOr9EeQ.js +0 -1
- package/dist/client/assets/chunk-WU5MYG2G-C4G-J1jx.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-DrjiwEWN.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DWHyuysv.js +0 -1
- package/dist/client/assets/eventmodeling-FCH6USID-DapHLQIh.js +0 -1
- package/dist/client/assets/gitGraph-WXDBUCRP-BTBN_hfb.js +0 -1
- package/dist/client/assets/index-DI9Vi2-3.js +0 -265
- package/dist/client/assets/index-DSap5tSX.css +0 -2
- package/dist/client/assets/info-J43DQDTF-CgiMJYKF.js +0 -1
- package/dist/client/assets/packet-YPE3B663-Bnwqjena.js +0 -1
- package/dist/client/assets/pie-LRSECV5Y-Ctj0ef-4.js +0 -1
- package/dist/client/assets/radar-GUYGQ44K-DE0H_saz.js +0 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-f36jgr8A.js +0 -1
- package/dist/client/assets/treeView-BLDUP644-CSAZL2OH.js +0 -1
- package/dist/client/assets/treemap-LRROVOQU-tYm4XKfP.js +0 -1
- package/dist/client/assets/wardley-L42UT6IY-DdiEFppm.js +0 -1
- /package/dist/client/assets/{array-B57E4cSs.js → array-BNor45A1.js} +0 -0
- /package/dist/client/assets/{chunk-NNHCCRGN-DRSAMe5H.js → chunk-NNHCCRGN-CZB-V-Gv.js} +0 -0
- /package/dist/client/assets/{cytoscape.esm-CVYSm6AJ.js → cytoscape.esm-B2lJcMSI.js} +0 -0
- /package/dist/client/assets/{defaultLocale-6WHJ3B09.js → defaultLocale-DPzUsThw.js} +0 -0
- /package/dist/client/assets/{dist-By2zKKTF.js → dist-CERhmQhR.js} +0 -0
- /package/dist/client/assets/{graphlib-B0DVs0bw.js → graphlib-BHOW-eWz.js} +0 -0
- /package/dist/client/assets/{init-DiYCGS1R.js → init-C0L3woqb.js} +0 -0
- /package/dist/client/assets/{katex-bgJu_0Fy.js → katex-CpjQsUQe.js} +0 -0
- /package/dist/client/assets/{path-DL_QDy2X.js → path-sMK4d_s9.js} +0 -0
- /package/dist/client/assets/{rough.esm-Bbn_-PMU.js → rough.esm-BoTisKeL.js} +0 -0
- /package/dist/client/assets/{src-Bi_byJUe.js → src-BRjoYtUW.js} +0 -0
package/dist/server/index.js
CHANGED
|
@@ -18,7 +18,7 @@ import { resolveChatSessionTarget } from './lib/chatTargets.js';
|
|
|
18
18
|
import { createChatMessage, getChatMessage, updateChatMessageContent } from './lib/chatMessages.js';
|
|
19
19
|
import { attachChatTicketProposalsToMessageContent } from './lib/chatTicketProposals.js';
|
|
20
20
|
import { createCorsOriginResolver, parseCorsOrigins } from './lib/cors.js';
|
|
21
|
-
import { abortGitOperation, branchExistsLocally, commitGitChanges, createWorktreeAsync, deleteWorktree, getBundleReviewDiffForCwd, getGitDiffData, getGitDiffFiles, getGitStatus, getGitUnifiedDiff, getProjectBranch, getProjectBranchSuggestions, getSelectedGitUnifiedDiff, hasKnownBranchRef, listUnpushedCommitsForCwd, pullGitBranch, pushCurrentBranch, rebaseGitBranchOntoBase, resolveExistingWorktreePath, resolveWorktreePath, summarizeTicketDiffFiles, switchProjectBranch, syncWorktreeFilesAsync, undoLatestUnpushedCommitForCwd } from './lib/git.js';
|
|
21
|
+
import { abortGitOperation, branchExistsLocally, commitGitChanges, createWorktreeAsync, deleteWorktree, getBundleReviewDiffForCwd, getGitDiffData, getGitDiffFiles, getGitStatus, getGitUnifiedDiff, getLocalProjectBranch, getProjectBranch, getProjectBranchSuggestions, getSelectedGitUnifiedDiff, hasKnownBranchRef, listUnpushedCommitsForCwd, pullGitBranch, pushCurrentBranch, rebaseGitBranchOntoBase, resolveExistingWorktreePath, resolveWorktreePath, summarizeTicketDiffFiles, switchProjectBranch, syncWorktreeFilesAsync, undoLatestUnpushedCommitForCwd } from './lib/git.js';
|
|
22
22
|
import config from './lib/config.js';
|
|
23
23
|
import { autoConfigProject, ProjectAutoConfigError } from './lib/projectAutoConfig.js';
|
|
24
24
|
import { analyzeRepoPath, createInitialCommit, ensureInitialCommitForWorktrees, initializeGitRepo, isInitialCommitRequiredError, } from './lib/projectRepos.js';
|
|
@@ -41,10 +41,12 @@ import { listProjectMemoryEvents } from './lib/projectMemoryEvents.js';
|
|
|
41
41
|
import { getProjectMemorySelectionForAgentRun, listProjectMemoryUsage } from './lib/projectMemorySelections.js';
|
|
42
42
|
import { decodeProjectMemoryCursor } from './lib/projectMemoryCursor.js';
|
|
43
43
|
import { getProjectFileSuggestions } from './lib/projectFileSuggestions.js';
|
|
44
|
+
import { getProjectFileManifest, ProjectFileContentError, ProjectFileTargetUnavailableError, readProjectFileContent } from './lib/projectFiles.js';
|
|
44
45
|
import { updateProjectTargetSelection } from './lib/projectTargets.js';
|
|
45
46
|
import { exportProjectMemoryArchive, importProjectMemoryArchive } from './lib/projectMemoryTransfer.js';
|
|
46
47
|
import { buildReviewFindingsResponse, computeDiffSignature, getLatestReviewRunForContext, listReviewFindings } from './lib/reviewFindings.js';
|
|
47
|
-
import {
|
|
48
|
+
import { createBuildOpenQuestionResolutionBatch, failBuildOpenQuestionBatch, formatBuildOpenQuestionAnswersPrompt, getLatestBuildOpenQuestionBatchStatus, hasActiveBuildOpenQuestionBatch, listAnsweredBuildOpenQuestionsForBatch, listBuildOpenQuestionsForBundle, loadBuildOpenQuestionsByBundleIds, setBuildOpenQuestionBatchStatus, } from './lib/buildOpenQuestions.js';
|
|
49
|
+
import { clearAutomationPause, countBlockingFindings, depthAtLeast, getBundleAutomationState, isAutomationDepth, recordAutomationPause, resolveAutomationConfig, } from './lib/ticketAutomation.js';
|
|
48
50
|
import { runAutomationCommitChain } from './lib/ticketAutomationCommit.js';
|
|
49
51
|
import { getDiscoveredSkills, normalizeSkillNames } from './lib/skills.js';
|
|
50
52
|
import { clearAllPreviewProxies } from './lib/previewProxy.js';
|
|
@@ -834,6 +836,87 @@ const resolveProjectFileSuggestionWorkspace = (project, options = {}) => {
|
|
|
834
836
|
cacheKey: `target:${workspace.target_kind}:${workspace.target_bundle_id ?? 'repo_root'}:${workspace.cwd}`,
|
|
835
837
|
};
|
|
836
838
|
};
|
|
839
|
+
const resolveWorkspaceFileWorkspace = (project, targetKind, bundleId, ticketId) => {
|
|
840
|
+
if (targetKind === 'repo_root') {
|
|
841
|
+
if (!existsSync(project.repo_path)) {
|
|
842
|
+
return { error: 'Repository target is unavailable', status: 409 };
|
|
843
|
+
}
|
|
844
|
+
return {
|
|
845
|
+
cwd: project.repo_path,
|
|
846
|
+
branch: getLocalProjectBranch(project),
|
|
847
|
+
project,
|
|
848
|
+
cacheKey: `workspace-files:${project.id}:repo_root:${project.repo_path}`,
|
|
849
|
+
target: { kind: 'repo_root', bundle_id: null, ticket_id: null },
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
if (targetKind === 'bundle') {
|
|
853
|
+
if (!bundleId) {
|
|
854
|
+
return { error: 'bundle_id is required for a bundle target', status: 400 };
|
|
855
|
+
}
|
|
856
|
+
const bundle = getBundle(bundleId, project.id);
|
|
857
|
+
if (!bundle || isProjectRootBundle(bundle)) {
|
|
858
|
+
return { error: 'Bundle target not found', status: 404 };
|
|
859
|
+
}
|
|
860
|
+
const cwd = resolveExistingWorktreePath(bundle.branch, project);
|
|
861
|
+
if (!cwd) {
|
|
862
|
+
return { error: 'Bundle target is unavailable', status: 409 };
|
|
863
|
+
}
|
|
864
|
+
return {
|
|
865
|
+
cwd,
|
|
866
|
+
branch: bundle.branch,
|
|
867
|
+
project,
|
|
868
|
+
cacheKey: `workspace-files:${project.id}:bundle:${bundle.id}:${cwd}`,
|
|
869
|
+
target: { kind: 'bundle', bundle_id: bundle.id, ticket_id: null },
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
if (!ticketId) {
|
|
873
|
+
return { error: 'ticket_id is required for a ticket target', status: 400 };
|
|
874
|
+
}
|
|
875
|
+
const ticket = getTicket(ticketId);
|
|
876
|
+
if (!ticket || ticket.project_id !== project.id) {
|
|
877
|
+
return { error: 'Ticket target not found', status: 404 };
|
|
878
|
+
}
|
|
879
|
+
const bundle = ticket.worktree_bundle_id ? getBundle(ticket.worktree_bundle_id, project.id) : null;
|
|
880
|
+
if (bundle && isProjectRootBundle(bundle)) {
|
|
881
|
+
if (!existsSync(project.repo_path)) {
|
|
882
|
+
return { error: 'Ticket target is unavailable', status: 409 };
|
|
883
|
+
}
|
|
884
|
+
return {
|
|
885
|
+
cwd: project.repo_path,
|
|
886
|
+
branch: getLocalProjectBranch(project),
|
|
887
|
+
project,
|
|
888
|
+
cacheKey: `workspace-files:${project.id}:ticket:${ticket.id}:${project.repo_path}`,
|
|
889
|
+
target: { kind: 'ticket', bundle_id: null, ticket_id: ticket.id },
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
const branch = resolveTicketBranch(ticket);
|
|
893
|
+
const cwd = branch ? resolveExistingWorktreePath(branch, project) : null;
|
|
894
|
+
if (!branch || !cwd) {
|
|
895
|
+
return { error: 'Ticket target is unavailable', status: 409 };
|
|
896
|
+
}
|
|
897
|
+
return {
|
|
898
|
+
cwd,
|
|
899
|
+
branch,
|
|
900
|
+
project,
|
|
901
|
+
cacheKey: `workspace-files:${project.id}:ticket:${ticket.id}:${cwd}`,
|
|
902
|
+
target: { kind: 'ticket', bundle_id: null, ticket_id: ticket.id },
|
|
903
|
+
};
|
|
904
|
+
};
|
|
905
|
+
const parseWorkspaceFileTarget = (req) => {
|
|
906
|
+
const targetKind = typeof req.query.target_kind === 'string' ? req.query.target_kind : '';
|
|
907
|
+
if (targetKind !== 'repo_root' && targetKind !== 'bundle' && targetKind !== 'ticket') {
|
|
908
|
+
return { error: 'target_kind must be repo_root, bundle, or ticket', status: 400 };
|
|
909
|
+
}
|
|
910
|
+
return {
|
|
911
|
+
targetKind,
|
|
912
|
+
bundleId: typeof req.query.bundle_id === 'string' ? req.query.bundle_id.trim() || null : null,
|
|
913
|
+
ticketId: typeof req.query.ticket_id === 'string' ? req.query.ticket_id.trim() || null : null,
|
|
914
|
+
};
|
|
915
|
+
};
|
|
916
|
+
const getWorkspaceFileTargetUnavailableMessage = (targetKind) => {
|
|
917
|
+
const targetName = targetKind === 'repo_root' ? 'Repository' : targetKind === 'bundle' ? 'Bundle' : 'Ticket';
|
|
918
|
+
return `${targetName} target is unavailable`;
|
|
919
|
+
};
|
|
837
920
|
const getProjectBranchStateForFooter = (project, options = {}) => {
|
|
838
921
|
const branchState = getProjectBranch(project, {
|
|
839
922
|
refreshRemote: options.refreshRemote,
|
|
@@ -1062,11 +1145,11 @@ const startBundlePullRequestDiscoveryIfNeeded = (project, bundle) => {
|
|
|
1062
1145
|
})();
|
|
1063
1146
|
bundlePullRequestDiscoveryInFlight.set(key, task);
|
|
1064
1147
|
};
|
|
1065
|
-
const serializeBundleRow = (projectId, bundle) => {
|
|
1148
|
+
const serializeBundleRow = (projectId, bundle, openQuestions = bundle ? loadBuildOpenQuestionsByBundleIds(projectId, [bundle.id]).get(bundle.id) ?? [] : [], resolvedProject) => {
|
|
1066
1149
|
if (!bundle) {
|
|
1067
1150
|
return null;
|
|
1068
1151
|
}
|
|
1069
|
-
const project = getProjectById(projectId);
|
|
1152
|
+
const project = resolvedProject ?? getProjectById(projectId);
|
|
1070
1153
|
const hasPreparedWorktree = bundle.kind === 'project_root'
|
|
1071
1154
|
? true
|
|
1072
1155
|
: Boolean(project && resolveExistingWorktreePath(bundle.branch, project));
|
|
@@ -1077,6 +1160,8 @@ const serializeBundleRow = (projectId, bundle) => {
|
|
|
1077
1160
|
? serializeBundlePullRequest(getBundlePullRequestRecord(bundle.id, projectId))
|
|
1078
1161
|
: null,
|
|
1079
1162
|
automation_config: resolveAutomationConfig(bundle, project),
|
|
1163
|
+
open_questions: openQuestions,
|
|
1164
|
+
build_open_question_batch_status: getLatestBuildOpenQuestionBatchStatus(projectId, bundle.id),
|
|
1080
1165
|
};
|
|
1081
1166
|
};
|
|
1082
1167
|
const serializeGitHubPullRequestSummary = (pullRequest) => {
|
|
@@ -1256,11 +1341,11 @@ const resolveBundleRenameEligibility = (project, bundle, tickets = listBundleTic
|
|
|
1256
1341
|
return { eligible: true, reason: null };
|
|
1257
1342
|
};
|
|
1258
1343
|
const serializeBundleDetails = (project, bundle, extras = {}) => {
|
|
1259
|
-
const
|
|
1344
|
+
const tickets = listDetailedBundleTickets(project.id, bundle.id);
|
|
1345
|
+
const serializedBundle = serializeBundleRow(project.id, bundle, tickets.flatMap(ticket => ticket.build_open_questions ?? []), project);
|
|
1260
1346
|
if (!serializedBundle) {
|
|
1261
1347
|
throw new Error(`Failed to serialize bundle ${bundle.id}`);
|
|
1262
1348
|
}
|
|
1263
|
-
const tickets = listDetailedBundleTickets(project.id, bundle.id);
|
|
1264
1349
|
const latestSessionTicket = resolveBundleLatestSessionTicket(tickets);
|
|
1265
1350
|
const resolvedTargetTicket = extras.target_ticket_id
|
|
1266
1351
|
? tickets.find(ticket => ticket.id === extras.target_ticket_id) ?? null
|
|
@@ -1389,6 +1474,9 @@ const reviewBundleTickets = (projectId, bundleId) => {
|
|
|
1389
1474
|
if (!bundle) {
|
|
1390
1475
|
return { ok: false, status: 404, error: 'Bundle not found' };
|
|
1391
1476
|
}
|
|
1477
|
+
if (hasActiveBuildOpenQuestionBatch(projectId, bundle.id)) {
|
|
1478
|
+
return { ok: false, status: 409, error: 'Build open question resolution is still in progress' };
|
|
1479
|
+
}
|
|
1392
1480
|
const result = transitionBundleToReview(projectId, bundle.id, io);
|
|
1393
1481
|
if (!result.ok) {
|
|
1394
1482
|
return { ok: false, status: result.status, error: result.error };
|
|
@@ -2630,11 +2718,12 @@ app.post('/projects/:id/spec-sessions/:sessionId/snapshots/:snapshotId/export',
|
|
|
2630
2718
|
return res.status(404).json({ error: 'Not found' });
|
|
2631
2719
|
}
|
|
2632
2720
|
try {
|
|
2721
|
+
const overwrite = req.body?.overwrite === true;
|
|
2633
2722
|
const collision = checkSpecExportCollision({
|
|
2634
2723
|
specSession,
|
|
2635
2724
|
repoPath: project.repo_path,
|
|
2636
2725
|
});
|
|
2637
|
-
if (collision) {
|
|
2726
|
+
if (collision && !overwrite) {
|
|
2638
2727
|
return res.status(409).json({
|
|
2639
2728
|
error: 'A spec with this title has already been exported.',
|
|
2640
2729
|
conflicting_path: collision,
|
|
@@ -2841,7 +2930,8 @@ app.get('/bundles', async (req, res) => {
|
|
|
2841
2930
|
timing.mark('ensure_project_root_bundle');
|
|
2842
2931
|
const bundles = listBundles(result.project.id);
|
|
2843
2932
|
timing.mark('list_bundles');
|
|
2844
|
-
const
|
|
2933
|
+
const openQuestionsByBundleId = loadBuildOpenQuestionsByBundleIds(result.project.id, bundles.map(bundle => bundle.id));
|
|
2934
|
+
const serializedBundles = bundles.map(bundle => serializeBundleRow(result.project.id, bundle, openQuestionsByBundleId.get(bundle.id) ?? [], result.project));
|
|
2845
2935
|
timing.mark('serialize_bundles');
|
|
2846
2936
|
timing.end({ count: serializedBundles.length });
|
|
2847
2937
|
res.json(serializedBundles);
|
|
@@ -3323,6 +3413,19 @@ app.patch('/bundles/:id', (req, res) => {
|
|
|
3323
3413
|
return res.status(400).json({ error: 'automation_review_fix_enabled must be a boolean' });
|
|
3324
3414
|
}
|
|
3325
3415
|
}
|
|
3416
|
+
let automationPauseOnOpenQuestions = bundle.automation_pause_on_open_questions ?? null;
|
|
3417
|
+
if (hasOwn(req.body, 'automation_pause_on_open_questions')) {
|
|
3418
|
+
const value = req.body.automation_pause_on_open_questions;
|
|
3419
|
+
if (value == null) {
|
|
3420
|
+
automationPauseOnOpenQuestions = null;
|
|
3421
|
+
}
|
|
3422
|
+
else if (typeof value === 'boolean') {
|
|
3423
|
+
automationPauseOnOpenQuestions = value ? 1 : 0;
|
|
3424
|
+
}
|
|
3425
|
+
else {
|
|
3426
|
+
return res.status(400).json({ error: 'automation_pause_on_open_questions must be a boolean' });
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3326
3429
|
let updated = null;
|
|
3327
3430
|
try {
|
|
3328
3431
|
updated = db.transaction(() => {
|
|
@@ -3339,6 +3442,7 @@ app.patch('/bundles/:id', (req, res) => {
|
|
|
3339
3442
|
projectId: result.project.id,
|
|
3340
3443
|
automationDepth,
|
|
3341
3444
|
automationReviewFixEnabled,
|
|
3445
|
+
automationPauseOnOpenQuestions,
|
|
3342
3446
|
});
|
|
3343
3447
|
})();
|
|
3344
3448
|
}
|
|
@@ -3379,6 +3483,99 @@ app.post('/bundles/:id/review', (req, res) => {
|
|
|
3379
3483
|
}
|
|
3380
3484
|
return res.json(reviewResult.details);
|
|
3381
3485
|
});
|
|
3486
|
+
app.post('/bundles/:id/build-open-questions/resolve', (req, res) => {
|
|
3487
|
+
const result = getRequiredProject(req);
|
|
3488
|
+
if ('error' in result) {
|
|
3489
|
+
return res.status(400).json({ error: result.error });
|
|
3490
|
+
}
|
|
3491
|
+
const bundle = getBundle(req.params.id, result.project.id);
|
|
3492
|
+
if (!bundle) {
|
|
3493
|
+
return res.status(404).json({ error: 'Bundle not found' });
|
|
3494
|
+
}
|
|
3495
|
+
const submitted = req.body?.resolutions;
|
|
3496
|
+
if (!Array.isArray(submitted) || submitted.length === 0) {
|
|
3497
|
+
return res.status(400).json({ error: 'resolutions must be a non-empty array' });
|
|
3498
|
+
}
|
|
3499
|
+
const resolutions = submitted.map((resolution) => {
|
|
3500
|
+
if (!resolution || typeof resolution !== 'object') {
|
|
3501
|
+
return null;
|
|
3502
|
+
}
|
|
3503
|
+
const value = resolution;
|
|
3504
|
+
return {
|
|
3505
|
+
questionId: typeof value.question_id === 'string' ? value.question_id : '',
|
|
3506
|
+
answer: typeof value.answer === 'string' ? value.answer : undefined,
|
|
3507
|
+
dismiss: value.dismiss === true,
|
|
3508
|
+
dismissalReason: typeof value.dismissal_reason === 'string' ? value.dismissal_reason : null,
|
|
3509
|
+
dismissedBy: 'user',
|
|
3510
|
+
};
|
|
3511
|
+
});
|
|
3512
|
+
if (resolutions.some(resolution => resolution === null || !resolution.questionId)) {
|
|
3513
|
+
return res.status(400).json({ error: 'Each resolution requires a question_id' });
|
|
3514
|
+
}
|
|
3515
|
+
const buildTickets = listBundleSliceTickets(result.project.id, bundle.id, 'build');
|
|
3516
|
+
if (buildTickets.length === 0
|
|
3517
|
+
|| buildTickets.some(ticket => ticket.agent_status !== 'done' && ticket.agent_status !== 'stopped')) {
|
|
3518
|
+
return res.status(409).json({ error: 'All build tickets in the bundle must be done or stopped before resolving questions' });
|
|
3519
|
+
}
|
|
3520
|
+
const created = createBuildOpenQuestionResolutionBatch({
|
|
3521
|
+
projectId: result.project.id,
|
|
3522
|
+
bundleId: bundle.id,
|
|
3523
|
+
resolutions: resolutions.filter((resolution) => resolution !== null),
|
|
3524
|
+
});
|
|
3525
|
+
if (!created.ok) {
|
|
3526
|
+
return res.status(409).json({
|
|
3527
|
+
error: created.error,
|
|
3528
|
+
...(created.code ? { code: created.code } : {}),
|
|
3529
|
+
});
|
|
3530
|
+
}
|
|
3531
|
+
const answeredQuestions = listAnsweredBuildOpenQuestionsForBatch(created.batch.id);
|
|
3532
|
+
if (answeredQuestions.length === 0) {
|
|
3533
|
+
clearAutomationPause(result.project.id, bundle.id);
|
|
3534
|
+
const automationConfig = resolveAutomationConfig(bundle, result.project);
|
|
3535
|
+
const reviewResult = depthAtLeast(automationConfig.depth, 'auto_review')
|
|
3536
|
+
? transitionBundleToReview(result.project.id, bundle.id, io, { automatic: true })
|
|
3537
|
+
: null;
|
|
3538
|
+
for (const ticket of listDetailedBundleTickets(result.project.id, bundle.id)) {
|
|
3539
|
+
io.emit('ticket:updated', ticket);
|
|
3540
|
+
}
|
|
3541
|
+
if (reviewResult && !reviewResult.ok) {
|
|
3542
|
+
return res.status(reviewResult.status).json({ error: reviewResult.error });
|
|
3543
|
+
}
|
|
3544
|
+
return res.json(serializeBundleDetails(result.project, bundle, {
|
|
3545
|
+
review_owner_ticket_id: reviewResult?.value?.id,
|
|
3546
|
+
target_ticket_id: reviewResult?.value?.id,
|
|
3547
|
+
}));
|
|
3548
|
+
}
|
|
3549
|
+
const targetTicketSource = resolveBundleTargetTicket(buildTickets);
|
|
3550
|
+
const targetTicket = targetTicketSource ? getTicket(targetTicketSource.id) : null;
|
|
3551
|
+
if (!targetTicket) {
|
|
3552
|
+
failBuildOpenQuestionBatch(created.batch.id, 'No completed Build ticket is available for the follow-up');
|
|
3553
|
+
recordAutomationPause(result.project.id, bundle.id, 'open_questions', 'Build open question follow-up could not be started');
|
|
3554
|
+
return res.status(409).json({ error: 'No completed Build ticket is available for the follow-up' });
|
|
3555
|
+
}
|
|
3556
|
+
const instruction = [
|
|
3557
|
+
'Apply this complete batch of answered Build requirements in one bundle-scoped pass. Preserve the grouping by originating ticket and do not work on dismissed questions.',
|
|
3558
|
+
formatBuildOpenQuestionAnswersPrompt(answeredQuestions),
|
|
3559
|
+
].join('\n\n');
|
|
3560
|
+
setBuildOpenQuestionBatchStatus(created.batch.id, 'running');
|
|
3561
|
+
const started = startFollowUp(targetTicket, io, instruction, 'build', {
|
|
3562
|
+
buildOpenQuestionBatchId: created.batch.id,
|
|
3563
|
+
});
|
|
3564
|
+
if (!started) {
|
|
3565
|
+
failBuildOpenQuestionBatch(created.batch.id, 'Build follow-up could not be started');
|
|
3566
|
+
recordAutomationPause(result.project.id, bundle.id, 'open_questions', 'Build open question follow-up could not be started');
|
|
3567
|
+
for (const ticket of listDetailedBundleTickets(result.project.id, bundle.id)) {
|
|
3568
|
+
io.emit('ticket:updated', ticket);
|
|
3569
|
+
}
|
|
3570
|
+
return res.status(409).json({ error: 'Build follow-up could not be started' });
|
|
3571
|
+
}
|
|
3572
|
+
for (const ticket of listDetailedBundleTickets(result.project.id, bundle.id)) {
|
|
3573
|
+
io.emit('ticket:updated', ticket);
|
|
3574
|
+
}
|
|
3575
|
+
return res.status(202).json(serializeBundleDetails(result.project, bundle, {
|
|
3576
|
+
target_ticket_id: targetTicket.id,
|
|
3577
|
+
}));
|
|
3578
|
+
});
|
|
3382
3579
|
app.post('/bundles/:id/undo-review', (req, res) => {
|
|
3383
3580
|
const result = getRequiredProject(req);
|
|
3384
3581
|
if ('error' in result) {
|
|
@@ -3482,10 +3679,21 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
3482
3679
|
if (!bundleState || !bundleState.automation_paused_reason) {
|
|
3483
3680
|
return res.status(409).json({ error: 'Bundle automation is not paused' });
|
|
3484
3681
|
}
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3682
|
+
const liveAutomationConfig = resolveAutomationConfig(bundle, result.project);
|
|
3683
|
+
if (hasActiveBuildOpenQuestionBatch(result.project.id, bundle.id)) {
|
|
3684
|
+
return res.status(409).json({ error: 'Build open question resolution is still in progress' });
|
|
3685
|
+
}
|
|
3686
|
+
if (bundleState.automation_paused_code === 'open_questions'
|
|
3687
|
+
&& liveAutomationConfig.pauseOnOpenQuestions
|
|
3688
|
+
&& listBuildOpenQuestionsForBundle(result.project.id, bundle.id).length > 0) {
|
|
3689
|
+
return res.status(409).json({ error: 'Resolve or dismiss all Build open questions before resuming automation' });
|
|
3690
|
+
}
|
|
3691
|
+
const reviewOwnerSource = bundleState.automation_paused_code === 'open_questions'
|
|
3692
|
+
? listBundleSliceTickets(result.project.id, bundle.id, 'build')
|
|
3693
|
+
.find(ticket => ticket.agent_status === 'done') ?? null
|
|
3694
|
+
: resolveBundleReviewOwner(result.project.id, bundle.id)
|
|
3695
|
+
?? listBundleSliceTickets(result.project.id, bundle.id, 'review')[0]
|
|
3696
|
+
?? null;
|
|
3489
3697
|
if (!reviewOwnerSource) {
|
|
3490
3698
|
return res.status(409).json({ error: 'No review ticket to resume from' });
|
|
3491
3699
|
}
|
|
@@ -3514,12 +3722,15 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
3514
3722
|
&& automationConfig.reviewFixEnabled
|
|
3515
3723
|
&& blockingCount > 0
|
|
3516
3724
|
&& iteration < automationConfig.reviewMaxIterations) {
|
|
3725
|
+
clearAutomationPause(result.project.id, bundle.id);
|
|
3517
3726
|
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');
|
|
3518
3727
|
}
|
|
3519
3728
|
else if (depthAtLeast(automationConfig.depth, 'auto_commit')) {
|
|
3729
|
+
clearAutomationPause(result.project.id, bundle.id);
|
|
3520
3730
|
await runAutomationCommitChain(owner, project, owner.bundle ?? null, automationConfig, io);
|
|
3521
3731
|
}
|
|
3522
3732
|
else {
|
|
3733
|
+
clearAutomationPause(result.project.id, bundle.id);
|
|
3523
3734
|
startReview(owner, io);
|
|
3524
3735
|
}
|
|
3525
3736
|
}
|
|
@@ -3528,6 +3739,7 @@ app.post('/bundles/:id/resume-automation', async (req, res) => {
|
|
|
3528
3739
|
if (!reviewResult.ok) {
|
|
3529
3740
|
return res.status(reviewResult.status).json({ error: reviewResult.error });
|
|
3530
3741
|
}
|
|
3742
|
+
clearAutomationPause(result.project.id, bundle.id);
|
|
3531
3743
|
}
|
|
3532
3744
|
else {
|
|
3533
3745
|
return res.status(409).json({ error: 'Ticket is not in a resumable state' });
|
|
@@ -3551,6 +3763,14 @@ app.post('/bundles/:id/dismiss-automation-pause', (req, res) => {
|
|
|
3551
3763
|
if (!bundleState || !bundleState.automation_paused_reason) {
|
|
3552
3764
|
return res.status(409).json({ error: 'Bundle automation is not paused' });
|
|
3553
3765
|
}
|
|
3766
|
+
if (hasActiveBuildOpenQuestionBatch(result.project.id, bundle.id)) {
|
|
3767
|
+
return res.status(409).json({ error: 'Build open question resolution is still in progress' });
|
|
3768
|
+
}
|
|
3769
|
+
if (bundleState.automation_paused_code === 'open_questions'
|
|
3770
|
+
&& resolveAutomationConfig(bundle, result.project).pauseOnOpenQuestions
|
|
3771
|
+
&& listBuildOpenQuestionsForBundle(result.project.id, bundle.id).length > 0) {
|
|
3772
|
+
return res.status(409).json({ error: 'Resolve or dismiss all Build open questions before dismissing this pause' });
|
|
3773
|
+
}
|
|
3554
3774
|
clearAutomationPause(result.project.id, bundle.id);
|
|
3555
3775
|
for (const ticket of listDetailedBundleTickets(result.project.id, bundle.id)) {
|
|
3556
3776
|
io.emit('ticket:updated', ticket);
|
|
@@ -4506,7 +4726,7 @@ app.post('/projects', (req, res) => {
|
|
|
4506
4726
|
return res.status(400).json({ error: validation.error });
|
|
4507
4727
|
}
|
|
4508
4728
|
const projectCount = Number(db.prepare('SELECT COUNT(*) AS count FROM projects').get()?.count ?? 0);
|
|
4509
|
-
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, scan_include_globs_json, scan_exclude_globs_json, worktree_sync, spec_aware_code_review_enabled, automation_depth, automation_review_fix_enabled, automation_review_severity, automation_review_max_iterations, } = validation.values;
|
|
4729
|
+
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, scan_include_globs_json, scan_exclude_globs_json, worktree_sync, spec_aware_code_review_enabled, automation_depth, automation_review_fix_enabled, automation_pause_on_open_questions, automation_review_severity, automation_review_max_iterations, } = validation.values;
|
|
4510
4730
|
if (!repo_path) {
|
|
4511
4731
|
return res.status(400).json({ error: 'repo_path must be a non-empty string' });
|
|
4512
4732
|
}
|
|
@@ -4542,11 +4762,12 @@ app.post('/projects', (req, res) => {
|
|
|
4542
4762
|
spec_aware_code_review_enabled,
|
|
4543
4763
|
automation_depth,
|
|
4544
4764
|
automation_review_fix_enabled,
|
|
4765
|
+
automation_pause_on_open_questions,
|
|
4545
4766
|
automation_review_severity,
|
|
4546
4767
|
automation_review_max_iterations
|
|
4547
4768
|
)
|
|
4548
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
4549
|
-
`).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, scan_include_globs_json ?? null, scan_exclude_globs_json ?? null, worktree_sync ?? null, spec_aware_code_review_enabled ?? 0, automation_depth ?? 'manual', automation_review_fix_enabled ?? 0, automation_review_severity ?? 'medium', automation_review_max_iterations ?? 3);
|
|
4769
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
4770
|
+
`).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, scan_include_globs_json ?? null, scan_exclude_globs_json ?? null, worktree_sync ?? null, spec_aware_code_review_enabled ?? 0, automation_depth ?? 'manual', automation_review_fix_enabled ?? 0, automation_pause_on_open_questions ?? 0, automation_review_severity ?? 'medium', automation_review_max_iterations ?? 3);
|
|
4550
4771
|
replaceProjectLinks(id, linked_project_ids ?? []);
|
|
4551
4772
|
ensureProjectRootBundle(id);
|
|
4552
4773
|
if (projectCount === 0) {
|
|
@@ -5316,6 +5537,60 @@ app.get('/projects/:id/file-suggestions', (req, res) => {
|
|
|
5316
5537
|
return res.status(500).json({ error: 'Unable to load project file suggestions' });
|
|
5317
5538
|
}
|
|
5318
5539
|
});
|
|
5540
|
+
app.get('/projects/:id/workspace-files', (req, res) => {
|
|
5541
|
+
const project = getProjectById(req.params.id);
|
|
5542
|
+
if (!project) {
|
|
5543
|
+
return res.status(404).json({ error: 'Not found' });
|
|
5544
|
+
}
|
|
5545
|
+
const target = parseWorkspaceFileTarget(req);
|
|
5546
|
+
if ('error' in target) {
|
|
5547
|
+
return res.status(target.status).json({ error: target.error });
|
|
5548
|
+
}
|
|
5549
|
+
const workspace = resolveWorkspaceFileWorkspace(project, target.targetKind, target.bundleId, target.ticketId);
|
|
5550
|
+
if ('error' in workspace) {
|
|
5551
|
+
return res.status(workspace.status).json({ error: workspace.error });
|
|
5552
|
+
}
|
|
5553
|
+
try {
|
|
5554
|
+
return res.json(getProjectFileManifest(workspace, { refresh: req.query.refresh === 'true' }));
|
|
5555
|
+
}
|
|
5556
|
+
catch (error) {
|
|
5557
|
+
if (error instanceof ProjectFileTargetUnavailableError) {
|
|
5558
|
+
return res.status(409).json({ error: getWorkspaceFileTargetUnavailableMessage(workspace.target.kind) });
|
|
5559
|
+
}
|
|
5560
|
+
console.error(`Failed to build workspace file manifest for project ${project.id}:`, error);
|
|
5561
|
+
return res.status(500).json({ error: 'Unable to load workspace files' });
|
|
5562
|
+
}
|
|
5563
|
+
});
|
|
5564
|
+
app.get('/projects/:id/workspace-files/content', (req, res) => {
|
|
5565
|
+
const project = getProjectById(req.params.id);
|
|
5566
|
+
if (!project) {
|
|
5567
|
+
return res.status(404).json({ error: 'Not found' });
|
|
5568
|
+
}
|
|
5569
|
+
const target = parseWorkspaceFileTarget(req);
|
|
5570
|
+
if ('error' in target) {
|
|
5571
|
+
return res.status(target.status).json({ error: target.error });
|
|
5572
|
+
}
|
|
5573
|
+
if (typeof req.query.path !== 'string') {
|
|
5574
|
+
return res.status(400).json({ error: 'path is required' });
|
|
5575
|
+
}
|
|
5576
|
+
const workspace = resolveWorkspaceFileWorkspace(project, target.targetKind, target.bundleId, target.ticketId);
|
|
5577
|
+
if ('error' in workspace) {
|
|
5578
|
+
return res.status(workspace.status).json({ error: workspace.error });
|
|
5579
|
+
}
|
|
5580
|
+
try {
|
|
5581
|
+
return res.json(readProjectFileContent(workspace, req.query.path));
|
|
5582
|
+
}
|
|
5583
|
+
catch (error) {
|
|
5584
|
+
if (error instanceof ProjectFileTargetUnavailableError) {
|
|
5585
|
+
return res.status(409).json({ error: getWorkspaceFileTargetUnavailableMessage(workspace.target.kind) });
|
|
5586
|
+
}
|
|
5587
|
+
if (error instanceof ProjectFileContentError) {
|
|
5588
|
+
return res.status(error.status).json({ error: error.message, reason: error.reason });
|
|
5589
|
+
}
|
|
5590
|
+
console.error(`Failed to read workspace file content for project ${project.id}:`, error);
|
|
5591
|
+
return res.status(500).json({ error: 'Unable to read workspace file' });
|
|
5592
|
+
}
|
|
5593
|
+
});
|
|
5319
5594
|
app.post('/projects/:id/git-pull', async (req, res) => {
|
|
5320
5595
|
const project = getProjectById(req.params.id);
|
|
5321
5596
|
if (!project) {
|
|
@@ -5658,7 +5933,7 @@ app.patch('/projects/:id', (req, res) => {
|
|
|
5658
5933
|
});
|
|
5659
5934
|
}
|
|
5660
5935
|
}
|
|
5661
|
-
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, scan_include_globs_json, scan_exclude_globs_json, worktree_sync, spec_aware_code_review_enabled, automation_depth, automation_review_fix_enabled, automation_review_severity, automation_review_max_iterations, } = validation.values;
|
|
5936
|
+
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, scan_include_globs_json, scan_exclude_globs_json, worktree_sync, spec_aware_code_review_enabled, automation_depth, automation_review_fix_enabled, automation_pause_on_open_questions, automation_review_severity, automation_review_max_iterations, } = validation.values;
|
|
5662
5937
|
db.prepare(`
|
|
5663
5938
|
UPDATE projects SET
|
|
5664
5939
|
name = ?,
|
|
@@ -5681,11 +5956,12 @@ app.patch('/projects/:id', (req, res) => {
|
|
|
5681
5956
|
spec_aware_code_review_enabled = ?,
|
|
5682
5957
|
automation_depth = ?,
|
|
5683
5958
|
automation_review_fix_enabled = ?,
|
|
5959
|
+
automation_pause_on_open_questions = ?,
|
|
5684
5960
|
automation_review_severity = ?,
|
|
5685
5961
|
automation_review_max_iterations = ?,
|
|
5686
5962
|
updated_at = CURRENT_TIMESTAMP
|
|
5687
5963
|
WHERE id = ?
|
|
5688
|
-
`).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, 'scan_include_globs_json') ? scan_include_globs_json : project.scan_include_globs_json, hasOwn(validation.values, 'scan_exclude_globs_json') ? scan_exclude_globs_json : project.scan_exclude_globs_json, hasOwn(validation.values, 'worktree_sync') ? worktree_sync : project.worktree_sync, hasOwn(validation.values, 'spec_aware_code_review_enabled') ? spec_aware_code_review_enabled : project.spec_aware_code_review_enabled, 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);
|
|
5964
|
+
`).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, 'scan_include_globs_json') ? scan_include_globs_json : project.scan_include_globs_json, hasOwn(validation.values, 'scan_exclude_globs_json') ? scan_exclude_globs_json : project.scan_exclude_globs_json, hasOwn(validation.values, 'worktree_sync') ? worktree_sync : project.worktree_sync, hasOwn(validation.values, 'spec_aware_code_review_enabled') ? spec_aware_code_review_enabled : project.spec_aware_code_review_enabled, 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_pause_on_open_questions') ? automation_pause_on_open_questions : project.automation_pause_on_open_questions, 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);
|
|
5689
5965
|
if (hasOwn(validation.values, 'linked_project_ids')) {
|
|
5690
5966
|
replaceProjectLinks(project.id, linked_project_ids ?? []);
|
|
5691
5967
|
}
|
|
@@ -6053,6 +6329,12 @@ app.post('/tickets/:id/retry', (req, res) => {
|
|
|
6053
6329
|
if (ticket.agent_status !== 'error') {
|
|
6054
6330
|
return res.status(400).json({ error: 'Ticket is not in error state' });
|
|
6055
6331
|
}
|
|
6332
|
+
if (ticket.state === 'review'
|
|
6333
|
+
&& ticket.project_id
|
|
6334
|
+
&& ticket.worktree_bundle_id
|
|
6335
|
+
&& hasActiveBuildOpenQuestionBatch(ticket.project_id, ticket.worktree_bundle_id)) {
|
|
6336
|
+
return res.status(409).json({ error: 'Build open question resolution is still in progress' });
|
|
6337
|
+
}
|
|
6056
6338
|
db.prepare('UPDATE tickets SET agent_status = NULL, agent_log = NULL, agent_message_id = NULL, previous_agent_log = NULL, previous_agent_message_id = NULL, streaming_response = NULL, build_completed_at = NULL, auto_park_dismissed_at = NULL, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(req.params.id);
|
|
6057
6339
|
const updated = getTicket(req.params.id);
|
|
6058
6340
|
io.emit('ticket:updated', updated);
|