@straiffi/archon 1.6.2 → 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.
Files changed (126) hide show
  1. package/dist/client/assets/{GitHubActionsDialog-YYq8JstU.js → GitHubActionsDialog-B9_WJBOp.js} +1 -1
  2. package/dist/client/assets/TestsDialog-DKvbjH_M.js +4 -0
  3. package/dist/client/assets/TicketChangesModal-C8rFoKjY.js +1 -0
  4. package/dist/client/assets/{arc-CnuHRbeq.js → arc-D2Nn9xIj.js} +1 -1
  5. package/dist/client/assets/architecture-7EHR7CIX-C-4Cp4ml.js +1 -0
  6. package/dist/client/assets/{architectureDiagram-3BPJPVTR-GehFBNFz.js → architectureDiagram-3BPJPVTR-DXRSRLL9.js} +1 -1
  7. package/dist/client/assets/{blockDiagram-GPEHLZMM-CTFXIE8q.js → blockDiagram-GPEHLZMM-D7EE6Dnv.js} +1 -1
  8. package/dist/client/assets/{c4Diagram-AAUBKEIU-BY9e8TiT.js → c4Diagram-AAUBKEIU-UhVzFafr.js} +1 -1
  9. package/dist/client/assets/channel-CNoEXkwa.js +1 -0
  10. package/dist/client/assets/{chunk-2J33WTMH-Ck8V3BxZ.js → chunk-2J33WTMH-obbfylVr.js} +1 -1
  11. package/dist/client/assets/{chunk-3OPIFGDE-DvZbg_ji.js → chunk-3OPIFGDE-DPVuS-uo.js} +1 -1
  12. package/dist/client/assets/{chunk-4BX2VUAB-_WBpCe0m.js → chunk-4BX2VUAB-CtLS8D71.js} +1 -1
  13. package/dist/client/assets/{chunk-4EGX6M5U-BBQHxMeA.js → chunk-4EGX6M5U-Btnm0QNe.js} +1 -1
  14. package/dist/client/assets/{chunk-55IACEB6-B-d-YzxI.js → chunk-55IACEB6-74CcJaub.js} +1 -1
  15. package/dist/client/assets/{chunk-5DO6E6H7-BA-oSVa-.js → chunk-5DO6E6H7-WLZ8L4ko.js} +1 -1
  16. package/dist/client/assets/{chunk-5ZQYHXKU-DZL6nSFA.js → chunk-5ZQYHXKU-Dsc03vy9.js} +1 -1
  17. package/dist/client/assets/{chunk-727SXJPM-DMy23TqS.js → chunk-727SXJPM-COE-iq_Z.js} +1 -1
  18. package/dist/client/assets/{chunk-AQP2D5EJ-Ch4juSfK.js → chunk-AQP2D5EJ-DnbAdr8m.js} +1 -1
  19. package/dist/client/assets/{chunk-BR22UD5L-prWX503j.js → chunk-BR22UD5L-Cou5beGe.js} +1 -1
  20. package/dist/client/assets/{chunk-BSJP7CBP-CNtf2uaW.js → chunk-BSJP7CBP-DJ57XcV5.js} +1 -1
  21. package/dist/client/assets/{chunk-CSCIHK7Q-BSL0qTfP.js → chunk-CSCIHK7Q-DbTzC_WS.js} +2 -2
  22. package/dist/client/assets/{chunk-FHYWG6QK-BQ8n0J0S.js → chunk-FHYWG6QK-Ba10e2kj.js} +1 -1
  23. package/dist/client/assets/{chunk-FMBD7UC4-CWQN4V4s.js → chunk-FMBD7UC4-CEo2wZMx.js} +1 -1
  24. package/dist/client/assets/{chunk-KSCS5N6A-C1zDfxY8.js → chunk-KSCS5N6A-xvviIayr.js} +1 -1
  25. package/dist/client/assets/{chunk-L5ZTLDWV-D8yXktZ8.js → chunk-L5ZTLDWV-DY4Q9Til.js} +1 -1
  26. package/dist/client/assets/{chunk-LZXEDZCA-CjmFVV-I.js → chunk-LZXEDZCA-Bbeq2DZs.js} +2 -2
  27. package/dist/client/assets/{chunk-MPE355IW-gHN8AuU_.js → chunk-MPE355IW-MqEt-J8G.js} +1 -1
  28. package/dist/client/assets/{chunk-MZUSXYTE-Clzaz1kK.js → chunk-MZUSXYTE-DdNi9lhx.js} +1 -1
  29. package/dist/client/assets/{chunk-N66VUXT2-CkkAOkdt.js → chunk-N66VUXT2-CjemTW4E.js} +1 -1
  30. package/dist/client/assets/{chunk-ND2GUHAM-Dc6gtC7r.js → chunk-ND2GUHAM-CylX1p3K.js} +1 -1
  31. package/dist/client/assets/{chunk-NZK2D7GU-CJMnwR03.js → chunk-NZK2D7GU-BTcALpQq.js} +1 -1
  32. package/dist/client/assets/{chunk-O5CBEL6O-BW2gf35w.js → chunk-O5CBEL6O-Be_5dk2_.js} +1 -1
  33. package/dist/client/assets/{chunk-PUPMXCY4-DAdQFv8j.js → chunk-PUPMXCY4-ZvwTw0To.js} +1 -1
  34. package/dist/client/assets/chunk-QZHKN3VN-LI_RZca2.js +1 -0
  35. package/dist/client/assets/{chunk-UIBZB4QT-BRR84OYN.js → chunk-UIBZB4QT-BTY9be9s.js} +1 -1
  36. package/dist/client/assets/{chunk-WCWK7LTN-CQU7Fs_W.js → chunk-WCWK7LTN-Db1n3n3-.js} +1 -1
  37. package/dist/client/assets/chunk-WU5MYG2G-DXPsSM4Q.js +1 -0
  38. package/dist/client/assets/{chunk-XPW4576I-DafcxlUi.js → chunk-XPW4576I-DObFPxjB.js} +1 -1
  39. package/dist/client/assets/classDiagram-4FO5ZUOK-BgmK8-LI.js +1 -0
  40. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Dj5LqBjo.js +1 -0
  41. package/dist/client/assets/{cose-bilkent-S5V4N54A-DhJHP9hr.js → cose-bilkent-S5V4N54A-MlOPiG6P.js} +1 -1
  42. package/dist/client/assets/{dagre-BM42HDAG-vmxXhZ0y.js → dagre-BM42HDAG-CABUW7xK.js} +1 -1
  43. package/dist/client/assets/{dagre-CdwzCXQr.js → dagre-BWNHuDdf.js} +1 -1
  44. package/dist/client/assets/{diagram-2AECGRRQ-Bf7MS2bc.js → diagram-2AECGRRQ-KvcNrpvf.js} +1 -1
  45. package/dist/client/assets/{diagram-5GNKFQAL-Br3zBMUu.js → diagram-5GNKFQAL-DQV6-I4e.js} +1 -1
  46. package/dist/client/assets/{diagram-KO2AKTUF-DTzQBils.js → diagram-KO2AKTUF-DYDk9roD.js} +1 -1
  47. package/dist/client/assets/{diagram-LMA3HP47-BT-zhfvW.js → diagram-LMA3HP47-Da-0u-HG.js} +1 -1
  48. package/dist/client/assets/{diagram-OG6HWLK6-BkJWuAdb.js → diagram-OG6HWLK6-CuD5eRBv.js} +1 -1
  49. package/dist/client/assets/{erDiagram-TEJ5UH35-DLuUFa06.js → erDiagram-TEJ5UH35-BL5dBE-b.js} +1 -1
  50. package/dist/client/assets/eventmodeling-FCH6USID-Dqb6QKoI.js +1 -0
  51. package/dist/client/assets/{flowDiagram-I6XJVG4X-DqxBy0Eu.js → flowDiagram-I6XJVG4X-Bke5cWs7.js} +1 -1
  52. package/dist/client/assets/{ganttDiagram-6RSMTGT7-lxV5wli4.js → ganttDiagram-6RSMTGT7-5OD-ayGn.js} +1 -1
  53. package/dist/client/assets/gitGraph-WXDBUCRP-DYEsqh93.js +1 -0
  54. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-iEzJEXy_.js → gitGraphDiagram-PVQCEYII-7Cf6FVfO.js} +1 -1
  55. package/dist/client/assets/index-D8EniXuq.css +2 -0
  56. package/dist/client/assets/index-DXuxAAzf.js +270 -0
  57. package/dist/client/assets/info-J43DQDTF-CfjXFZai.js +1 -0
  58. package/dist/client/assets/{infoDiagram-5YYISTIA-mjHIc-rc.js → infoDiagram-5YYISTIA-BchMN9V-.js} +1 -1
  59. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-C3mJymgB.js → ishikawaDiagram-YF4QCWOH-D6TchjQy.js} +1 -1
  60. package/dist/client/assets/{journeyDiagram-JHISSGLW-Bl7pH31i.js → journeyDiagram-JHISSGLW-CwEcoeX-.js} +1 -1
  61. package/dist/client/assets/{kanban-definition-UN3LZRKU-iL68fF8o.js → kanban-definition-UN3LZRKU-S2V9CLGR.js} +1 -1
  62. package/dist/client/assets/{line-BbGkhDvA.js → line-DRheChzQ.js} +1 -1
  63. package/dist/client/assets/{linear-DFIW-KnM.js → linear-DT4rSZvy.js} +1 -1
  64. package/dist/client/assets/{mermaid-parser.core-3NnYiOiR.js → mermaid-parser.core-CN13XjLe.js} +2 -2
  65. package/dist/client/assets/{mermaid.core-BdUaWj-Y.js → mermaid.core-JhdSpoF5.js} +3 -3
  66. package/dist/client/assets/{mindmap-definition-RKZ34NQL-sDh1lwIs.js → mindmap-definition-RKZ34NQL-657TKwRB.js} +1 -1
  67. package/dist/client/assets/{ordinal-C1FS1Sfe.js → ordinal-k--hYEme.js} +1 -1
  68. package/dist/client/assets/packet-YPE3B663-Dv0HkRHU.js +1 -0
  69. package/dist/client/assets/pie-LRSECV5Y-S5zPzZFN.js +1 -0
  70. package/dist/client/assets/{pieDiagram-4H26LBE5-CmkeWRGt.js → pieDiagram-4H26LBE5-DJ8eQ94X.js} +1 -1
  71. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-CusAc0KA.js → quadrantDiagram-W4KKPZXB-GpYFqyb1.js} +1 -1
  72. package/dist/client/assets/radar-GUYGQ44K-B7psLv22.js +1 -0
  73. package/dist/client/assets/{requirementDiagram-4Y6WPE33-DRjpIANs.js → requirementDiagram-4Y6WPE33-COZFWgq1.js} +1 -1
  74. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-DNpufJOV.js → sankeyDiagram-5OEKKPKP-BdCHyfcQ.js} +1 -1
  75. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-BD5zE-V4.js → sequenceDiagram-3UESZ5HK-DAh3Ogx0.js} +1 -1
  76. package/dist/client/assets/{stateDiagram-AJRCARHV-fCUNQluV.js → stateDiagram-AJRCARHV-Ck8Yie8_.js} +1 -1
  77. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-B6zibRqD.js +1 -0
  78. package/dist/client/assets/{timeline-definition-PNZ67QCA-C4u3WsSc.js → timeline-definition-PNZ67QCA-kYr9DNtP.js} +1 -1
  79. package/dist/client/assets/treeView-BLDUP644-B8H0kT67.js +1 -0
  80. package/dist/client/assets/treemap-LRROVOQU-DPLhFHnh.js +1 -0
  81. package/dist/client/assets/{vennDiagram-CIIHVFJN-HdgYgPYH.js → vennDiagram-CIIHVFJN-CC_SlRiC.js} +1 -1
  82. package/dist/client/assets/wardley-L42UT6IY-B5-Vb08V.js +1 -0
  83. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-mtIAqcj8.js → wardleyDiagram-YWT4CUSO-CZ17ccSN.js} +1 -1
  84. package/dist/client/assets/{xychartDiagram-2RQKCTM6-CekNHwM1.js → xychartDiagram-2RQKCTM6-Blqaj-ci.js} +1 -1
  85. package/dist/client/index.html +2 -2
  86. package/dist/server/index.js +139 -2
  87. package/dist/server/index.js.map +1 -1
  88. package/dist/server/lib/git.js +19 -3
  89. package/dist/server/lib/git.js.map +1 -1
  90. package/dist/server/lib/previewProxy.js +5 -0
  91. package/dist/server/lib/previewProxy.js.map +1 -1
  92. package/dist/server/lib/projectFiles.js +313 -0
  93. package/dist/server/lib/projectFiles.js.map +1 -0
  94. package/dist/server/workers/followUp.js +1 -0
  95. package/dist/server/workers/followUp.js.map +1 -1
  96. package/package.json +1 -1
  97. package/dist/client/assets/TestsDialog-BDV84HfF.js +0 -4
  98. package/dist/client/assets/architecture-7EHR7CIX-tSrokdFP.js +0 -1
  99. package/dist/client/assets/channel-BHDgEkkI.js +0 -1
  100. package/dist/client/assets/chunk-QZHKN3VN-biOr9EeQ.js +0 -1
  101. package/dist/client/assets/chunk-WU5MYG2G-6nbIEzjM.js +0 -1
  102. package/dist/client/assets/classDiagram-4FO5ZUOK-eaJS9esZ.js +0 -1
  103. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DjTlDKs4.js +0 -1
  104. package/dist/client/assets/eventmodeling-FCH6USID-DapHLQIh.js +0 -1
  105. package/dist/client/assets/gitGraph-WXDBUCRP-BTBN_hfb.js +0 -1
  106. package/dist/client/assets/index-B3KA0m7e.css +0 -2
  107. package/dist/client/assets/index-BnMFJ-iq.js +0 -265
  108. package/dist/client/assets/info-J43DQDTF-CgiMJYKF.js +0 -1
  109. package/dist/client/assets/packet-YPE3B663-Bnwqjena.js +0 -1
  110. package/dist/client/assets/pie-LRSECV5Y-Ctj0ef-4.js +0 -1
  111. package/dist/client/assets/radar-GUYGQ44K-DE0H_saz.js +0 -1
  112. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CaE9ItcI.js +0 -1
  113. package/dist/client/assets/treeView-BLDUP644-CSAZL2OH.js +0 -1
  114. package/dist/client/assets/treemap-LRROVOQU-tYm4XKfP.js +0 -1
  115. package/dist/client/assets/wardley-L42UT6IY-DdiEFppm.js +0 -1
  116. /package/dist/client/assets/{array-B57E4cSs.js → array-BNor45A1.js} +0 -0
  117. /package/dist/client/assets/{chunk-NNHCCRGN-DRSAMe5H.js → chunk-NNHCCRGN-CZB-V-Gv.js} +0 -0
  118. /package/dist/client/assets/{cytoscape.esm-CVYSm6AJ.js → cytoscape.esm-B2lJcMSI.js} +0 -0
  119. /package/dist/client/assets/{defaultLocale-6WHJ3B09.js → defaultLocale-DPzUsThw.js} +0 -0
  120. /package/dist/client/assets/{dist-By2zKKTF.js → dist-CERhmQhR.js} +0 -0
  121. /package/dist/client/assets/{graphlib-B0DVs0bw.js → graphlib-BHOW-eWz.js} +0 -0
  122. /package/dist/client/assets/{init-DiYCGS1R.js → init-C0L3woqb.js} +0 -0
  123. /package/dist/client/assets/{katex-bgJu_0Fy.js → katex-CpjQsUQe.js} +0 -0
  124. /package/dist/client/assets/{path-DL_QDy2X.js → path-sMK4d_s9.js} +0 -0
  125. /package/dist/client/assets/{rough.esm-Bbn_-PMU.js → rough.esm-BoTisKeL.js} +0 -0
  126. /package/dist/client/assets/{src-Bi_byJUe.js → src-BRjoYtUW.js} +0 -0
@@ -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,6 +41,7 @@ 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';
@@ -835,6 +836,87 @@ const resolveProjectFileSuggestionWorkspace = (project, options = {}) => {
835
836
  cacheKey: `target:${workspace.target_kind}:${workspace.target_bundle_id ?? 'repo_root'}:${workspace.cwd}`,
836
837
  };
837
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
+ };
838
920
  const getProjectBranchStateForFooter = (project, options = {}) => {
839
921
  const branchState = getProjectBranch(project, {
840
922
  refreshRemote: options.refreshRemote,
@@ -2636,11 +2718,12 @@ app.post('/projects/:id/spec-sessions/:sessionId/snapshots/:snapshotId/export',
2636
2718
  return res.status(404).json({ error: 'Not found' });
2637
2719
  }
2638
2720
  try {
2721
+ const overwrite = req.body?.overwrite === true;
2639
2722
  const collision = checkSpecExportCollision({
2640
2723
  specSession,
2641
2724
  repoPath: project.repo_path,
2642
2725
  });
2643
- if (collision) {
2726
+ if (collision && !overwrite) {
2644
2727
  return res.status(409).json({
2645
2728
  error: 'A spec with this title has already been exported.',
2646
2729
  conflicting_path: collision,
@@ -5454,6 +5537,60 @@ app.get('/projects/:id/file-suggestions', (req, res) => {
5454
5537
  return res.status(500).json({ error: 'Unable to load project file suggestions' });
5455
5538
  }
5456
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
+ });
5457
5594
  app.post('/projects/:id/git-pull', async (req, res) => {
5458
5595
  const project = getProjectById(req.params.id);
5459
5596
  if (!project) {