trellis 3.2.0 → 3.2.2

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 (258) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +61 -171
  3. package/dist/browser/index.d.ts +18 -0
  4. package/dist/browser/index.d.ts.map +1 -0
  5. package/dist/browser/index.js +95 -0
  6. package/dist/{chunk-3ETIIPPB.js → chunk-2W3MHTMG.js} +190 -27
  7. package/dist/{chunk-MBNBTJCR.js → chunk-6GEZ6DN5.js} +2 -2
  8. package/dist/chunk-7ZFRVCUE.js +211 -0
  9. package/dist/chunk-CAG4FULI.js +121 -0
  10. package/dist/{chunk-GD5QJXSS.js → chunk-FWRS4CSC.js} +2 -2
  11. package/dist/{chunk-36VSSTL7.js → chunk-GU374KWZ.js} +1 -1
  12. package/dist/{chunk-3GDRZTAW.js → chunk-H6JB3PZ3.js} +1 -1
  13. package/dist/{chunk-ZHVI7BQN.js → chunk-IZX2PLIB.js} +191 -28
  14. package/dist/chunk-J2TXUFCZ.js +289 -0
  15. package/dist/{chunk-BJ7EZ7FP.js → chunk-JHHEXEDM.js} +17 -0
  16. package/dist/{chunk-X762XWVC.js → chunk-KIJITUZB.js} +1 -1
  17. package/dist/{chunk-7WQGTFVJ.js → chunk-L5N5PR2S.js} +130 -75
  18. package/dist/{chunk-BXSTJVE3.js → chunk-N6MYZT4O.js} +2 -1
  19. package/dist/{chunk-KKDRMIV6.js → chunk-QB5ISE47.js} +1 -1
  20. package/dist/{chunk-HO4TURXW.js → chunk-S3XLFZ27.js} +28 -11
  21. package/dist/{chunk-BQBUVMRN.js → chunk-S6GBJ2ZB.js} +25 -14
  22. package/dist/{chunk-IUWVEEOX.js → chunk-SGMDTWJJ.js} +4 -0
  23. package/dist/{chunk-FZVCXDYW.js → chunk-UQISRW6T.js} +65 -12
  24. package/dist/{chunk-B3ZIFA2O.js → chunk-VBYUTRXX.js} +1 -1
  25. package/dist/cli/deploy-cli.d.ts +18 -0
  26. package/dist/cli/deploy-cli.d.ts.map +1 -0
  27. package/dist/cli/index.d.ts +1 -1
  28. package/dist/cli/index.d.ts.map +1 -1
  29. package/dist/cli/index.js +153 -100
  30. package/dist/client/index.browser.d.ts +29 -0
  31. package/dist/client/index.browser.d.ts.map +1 -0
  32. package/dist/client/index.browser.js +64 -0
  33. package/dist/client/index.js +10 -10
  34. package/dist/client/sdk.browser.d.ts +104 -0
  35. package/dist/client/sdk.browser.d.ts.map +1 -0
  36. package/dist/{sdk-LZ7W3EEY.js → client/sdk.browser.js} +2 -2
  37. package/dist/client/sdk.js +1 -1
  38. package/dist/core/index.js +6 -6
  39. package/dist/db/index.js +18 -18
  40. package/dist/db/inspector.js +6 -6
  41. package/dist/db/trellis.css +1 -1
  42. package/dist/decisions/index.js +2 -2
  43. package/dist/deploy-LQ7GUGJ5.js +9 -0
  44. package/dist/{deploy-66EC752W.js → deploy-YNVG5E4E.js} +1 -1
  45. package/dist/deploy-cli-24BCHAXY.js +67 -0
  46. package/dist/deploy-cli-C35HTITO.js +67 -0
  47. package/dist/embeddings/auto-embed.d.ts.map +1 -1
  48. package/dist/embeddings/index.js +1 -1
  49. package/dist/embeddings/store.d.ts.map +1 -1
  50. package/dist/engine.d.ts +27 -7
  51. package/dist/engine.d.ts.map +1 -1
  52. package/dist/index.js +6 -6
  53. package/dist/mcp/server.d.ts.map +1 -1
  54. package/dist/react/index.js +2 -2
  55. package/dist/react/schema-hooks.js +7 -7
  56. package/dist/{remote-manager-SG545WNY.js → remote-manager-ODE3T2KR.js} +5 -5
  57. package/dist/scaffold/seed.d.ts.map +1 -1
  58. package/dist/scaffold/write.d.ts +1 -1
  59. package/dist/scaffold/write.d.ts.map +1 -1
  60. package/dist/schema/index.js +4 -3
  61. package/dist/server/deploy-meta.d.ts +25 -0
  62. package/dist/server/deploy-meta.d.ts.map +1 -0
  63. package/dist/server/deploy.d.ts +7 -2
  64. package/dist/server/deploy.d.ts.map +1 -1
  65. package/dist/server/index.d.ts +5 -2
  66. package/dist/server/index.d.ts.map +1 -1
  67. package/dist/server/index.js +52 -18
  68. package/dist/server/realtime.d.ts +4 -1
  69. package/dist/server/realtime.d.ts.map +1 -1
  70. package/dist/server/server.d.ts +1 -0
  71. package/dist/server/server.d.ts.map +1 -1
  72. package/dist/server/sprites.d.ts +18 -1
  73. package/dist/server/sprites.d.ts.map +1 -1
  74. package/dist/server/tenancy.d.ts +4 -0
  75. package/dist/server/tenancy.d.ts.map +1 -1
  76. package/dist/server/usage-meter.d.ts +53 -0
  77. package/dist/server/usage-meter.d.ts.map +1 -0
  78. package/dist/{server-IGBIIJSF.js → server-MB3OPSPI.js} +6 -1
  79. package/dist/{server-BOH7M4TT.js → server-ZVWGLLRF.js} +7 -2
  80. package/dist/sprites-KQGTGM6W.js +27 -0
  81. package/dist/{tenancy-S676GLID.js → tenancy-NBLLGCNJ.js} +1 -1
  82. package/dist/{tenancy-OP34NFFS.js → tenancy-POMWQP6M.js} +5 -3
  83. package/dist/vcs/index.js +3 -3
  84. package/dist/vcs/types.d.ts +2 -0
  85. package/dist/vcs/types.d.ts.map +1 -1
  86. package/package.json +17 -6
  87. package/dist/better-sqlite-backend-PII64VE6.js +0 -159
  88. package/dist/better-sqlite-backend-VUBVQ7SN.js +0 -209
  89. package/dist/better-sqlite-backend-Y3AQN65E.js +0 -159
  90. package/dist/better-sqlite-backend-Y3M7DXOX.js +0 -159
  91. package/dist/boot-middleware-FJDGVUM4.js +0 -9
  92. package/dist/boot-middleware-MLJUTLUM.js +0 -9
  93. package/dist/boot-middleware-ZZPDZNYR.js +0 -9
  94. package/dist/chunk-276LKLZR.js +0 -359
  95. package/dist/chunk-2CX5UCRV.js +0 -111
  96. package/dist/chunk-2ESYSVXG.js +0 -48
  97. package/dist/chunk-2I5JU73Y.js +0 -340
  98. package/dist/chunk-2UNSRRNB.js +0 -171
  99. package/dist/chunk-335UEHQR.js +0 -907
  100. package/dist/chunk-4CF2L46N.js +0 -29
  101. package/dist/chunk-5CINODKT.js +0 -41
  102. package/dist/chunk-5ESGWJ5L.js +0 -45
  103. package/dist/chunk-5FGLQGMF.js +0 -1676
  104. package/dist/chunk-5IQZFITY.js +0 -357
  105. package/dist/chunk-5JWFAQDW.js +0 -150
  106. package/dist/chunk-62UHTVU3.js +0 -141
  107. package/dist/chunk-66UJQT2I.js +0 -35
  108. package/dist/chunk-6RNNVOAS.js +0 -355
  109. package/dist/chunk-6X23PZ6H.js +0 -969
  110. package/dist/chunk-7C7LL6MB.js +0 -315
  111. package/dist/chunk-7F4HM3JG.js +0 -394
  112. package/dist/chunk-7KJ7T4D3.js +0 -910
  113. package/dist/chunk-7QQQNUB5.js +0 -92
  114. package/dist/chunk-7QWJBS72.js +0 -946
  115. package/dist/chunk-7R36OZK3.js +0 -957
  116. package/dist/chunk-7UPKCOHE.js +0 -946
  117. package/dist/chunk-A3BONW34.js +0 -35
  118. package/dist/chunk-AHMDVHBL.js +0 -141
  119. package/dist/chunk-AW6A4OZY.js +0 -171
  120. package/dist/chunk-BJ5EKCJ5.js +0 -458
  121. package/dist/chunk-BRGYDO7D.js +0 -223
  122. package/dist/chunk-C5KPSEDN.js +0 -359
  123. package/dist/chunk-CCTNYTUC.js +0 -940
  124. package/dist/chunk-CYQ2OVD2.js +0 -35
  125. package/dist/chunk-DDAK7YOE.js +0 -299
  126. package/dist/chunk-DRILKBY6.js +0 -965
  127. package/dist/chunk-DXE24HNN.js +0 -45
  128. package/dist/chunk-E7ICUNW7.js +0 -945
  129. package/dist/chunk-EAL2PUOE.js +0 -10022
  130. package/dist/chunk-ELT7MLZB.js +0 -109
  131. package/dist/chunk-EWEIRF6G.js +0 -171
  132. package/dist/chunk-F5VDGQJC.js +0 -120
  133. package/dist/chunk-FCPL3IEQ.js +0 -98
  134. package/dist/chunk-FDPKYSV7.js +0 -96
  135. package/dist/chunk-FES3TC24.js +0 -171
  136. package/dist/chunk-FOWKR5TW.js +0 -129
  137. package/dist/chunk-G3XIHPSQ.js +0 -361
  138. package/dist/chunk-GKJLYLVU.js +0 -359
  139. package/dist/chunk-GNXHXNVQ.js +0 -113
  140. package/dist/chunk-GSR6ILTM.js +0 -277
  141. package/dist/chunk-GWRIMRSX.js +0 -45
  142. package/dist/chunk-H72UW2HJ.js +0 -45
  143. package/dist/chunk-HGI74PXB.js +0 -7417
  144. package/dist/chunk-HGRA37LO.js +0 -145
  145. package/dist/chunk-HXSRQO3L.js +0 -145
  146. package/dist/chunk-IG2X2BVO.js +0 -373
  147. package/dist/chunk-IIJVDD54.js +0 -373
  148. package/dist/chunk-IX4BBOIT.js +0 -394
  149. package/dist/chunk-IXU4RMQM.js +0 -349
  150. package/dist/chunk-J6OUIION.js +0 -857
  151. package/dist/chunk-K46RNFDE.js +0 -171
  152. package/dist/chunk-KAGU2XMY.js +0 -359
  153. package/dist/chunk-KCVYJNVX.js +0 -394
  154. package/dist/chunk-KYLIQ3YI.js +0 -399
  155. package/dist/chunk-KZYJJIRN.js +0 -946
  156. package/dist/chunk-LFOX744K.js +0 -96
  157. package/dist/chunk-LHOVJEWR.js +0 -7403
  158. package/dist/chunk-LOPXTPF3.js +0 -120
  159. package/dist/chunk-LZR3VIFU.js +0 -907
  160. package/dist/chunk-MCKGQKYU.js +0 -15
  161. package/dist/chunk-MU6EOS6F.js +0 -0
  162. package/dist/chunk-MWRQ2TXT.js +0 -357
  163. package/dist/chunk-NO73OK2I.js +0 -102
  164. package/dist/chunk-NPE2YJHK.js +0 -394
  165. package/dist/chunk-NSDXU6QX.js +0 -2213
  166. package/dist/chunk-NXIRDSZ6.js +0 -145
  167. package/dist/chunk-NZWFNHCS.js +0 -407
  168. package/dist/chunk-OB4CMMX6.js +0 -260
  169. package/dist/chunk-OBKU74FU.js +0 -7411
  170. package/dist/chunk-OQNDWRDE.js +0 -145
  171. package/dist/chunk-OVWAJMB6.js +0 -394
  172. package/dist/chunk-PDHLBTTD.js +0 -45
  173. package/dist/chunk-PEQNRKLG.js +0 -210
  174. package/dist/chunk-QERIS7QY.js +0 -7403
  175. package/dist/chunk-QHA2DM3C.js +0 -373
  176. package/dist/chunk-QLM4HSYC.js +0 -45
  177. package/dist/chunk-S2OT3TQJ.js +0 -283
  178. package/dist/chunk-S5PHSN5K.js +0 -907
  179. package/dist/chunk-SIAA4J6H.js +0 -21
  180. package/dist/chunk-SQBNLUIA.js +0 -171
  181. package/dist/chunk-STLN6A6B.js +0 -355
  182. package/dist/chunk-U7HV7GU2.js +0 -17
  183. package/dist/chunk-UGZHOGRA.js +0 -39
  184. package/dist/chunk-UICF66FS.js +0 -359
  185. package/dist/chunk-UK3EQREE.js +0 -1078
  186. package/dist/chunk-UPKPURVY.js +0 -394
  187. package/dist/chunk-URC74VCN.js +0 -959
  188. package/dist/chunk-V3G7Q3QQ.js +0 -102
  189. package/dist/chunk-VUJ6SA55.js +0 -303
  190. package/dist/chunk-VUJQLIML.js +0 -141
  191. package/dist/chunk-VWKLKLYB.js +0 -267
  192. package/dist/chunk-VYC3RZMK.js +0 -277
  193. package/dist/chunk-WMERMSWI.js +0 -154
  194. package/dist/chunk-WMR777PO.js +0 -2213
  195. package/dist/chunk-X46IECAC.js +0 -517
  196. package/dist/chunk-XEN3OEYY.js +0 -2234
  197. package/dist/chunk-XZQ66K7A.js +0 -124
  198. package/dist/chunk-YY3GF7DH.js +0 -7411
  199. package/dist/chunk-ZE5R3H4C.js +0 -907
  200. package/dist/chunk-ZMN5LUB7.js +0 -7411
  201. package/dist/chunk-ZUV4MD5U.js +0 -94
  202. package/dist/config-KMY6RRK3.js +0 -19
  203. package/dist/config-VDMK2DUJ.js +0 -19
  204. package/dist/deploy-SO7AFK6L.js +0 -9
  205. package/dist/engine-7GXBM4IT.js +0 -7
  206. package/dist/engine-NSR6BIF7.js +0 -7
  207. package/dist/factory-2P64OC7M.js +0 -34
  208. package/dist/factory-YP4ZZK5K.js +0 -34
  209. package/dist/import-AJLYHFIA.js +0 -10
  210. package/dist/launch-explorer-AI6L2AWJ.js +0 -91
  211. package/dist/query-GJL3CVGN.js +0 -31
  212. package/dist/query-MBJSK3EB.js +0 -31
  213. package/dist/query-TJHEVSKL.js +0 -31
  214. package/dist/query-WUIKOHPE.js +0 -524
  215. package/dist/remote-manager-BLJZ6KF6.js +0 -224
  216. package/dist/remote-manager-BPYE46SI.js +0 -223
  217. package/dist/remote-manager-FMESEAFA.js +0 -224
  218. package/dist/remote-manager-K2WZ5RHY.js +0 -223
  219. package/dist/remote-manager-SG5PQKCH.js +0 -224
  220. package/dist/remote-manager-WGP6MIQZ.js +0 -224
  221. package/dist/sdk-3KBB3E6V.js +0 -16
  222. package/dist/sdk-4ZQO6UTU.js +0 -16
  223. package/dist/sdk-6JA6GV3G.js +0 -16
  224. package/dist/sdk-6VTP5UTZ.js +0 -16
  225. package/dist/sdk-7VS6TYCD.js +0 -16
  226. package/dist/sdk-HQ3FTLRT.js +0 -16
  227. package/dist/sdk-HZ7NU3XE.js +0 -15
  228. package/dist/sdk-KBQE2OXT.js +0 -16
  229. package/dist/sdk-MSCDZ7JU.js +0 -15
  230. package/dist/sdk-WNZOGO3S.js +0 -15
  231. package/dist/sdk-YLV33ZCW.js +0 -9
  232. package/dist/sdk-ZSUD2LLJ.js +0 -16
  233. package/dist/server-AFXFFBWA.js +0 -11
  234. package/dist/server-AGHC4VUL.js +0 -11
  235. package/dist/server-DBUY2KY5.js +0 -11
  236. package/dist/server-GWBQJPRG.js +0 -11
  237. package/dist/server-JZZFULVU.js +0 -11
  238. package/dist/server-L5XAF4M5.js +0 -11
  239. package/dist/server-NYFUHANA.js +0 -12
  240. package/dist/server-OABUB2LO.js +0 -12
  241. package/dist/server-P2CKZWWV.js +0 -12
  242. package/dist/sprites-XVSMQLSI.js +0 -15
  243. package/dist/sql-wasm-FORCZFHZ.js +0 -2148
  244. package/dist/sql-wasm-JZH76RLO.js +0 -2148
  245. package/dist/sqljs-backend-CJQOIQYE.js +0 -266
  246. package/dist/sqljs-backend-KWXBUDUO.js +0 -266
  247. package/dist/state-demo.bundle.js +0 -1033
  248. package/dist/tenancy-CBWYEVUB.js +0 -11
  249. package/dist/tenancy-FO6WVYFN.js +0 -13
  250. package/dist/tenancy-FWDBESMX.js +0 -14
  251. package/dist/tenancy-HGPIS5QA.js +0 -13
  252. package/dist/tenancy-HXFALA37.js +0 -14
  253. package/dist/tenancy-V7ARWNNV.js +0 -14
  254. package/dist/tenancy-ZDN4GXK5.js +0 -13
  255. package/dist/tenancy-ZT5VMY7B.js +0 -14
  256. package/dist/vm-config-YZRJ3KUB.js +0 -21
  257. /package/dist/{chunk-2BRTCEHB.js → chunk-PZ4XYZ4J.js} +0 -0
  258. /package/dist/{chunk-EO3J7GRP.js → chunk-WXSWA3MV.js} +0 -0
@@ -1,2213 +0,0 @@
1
- import {
2
- createVcsOp,
3
- criterionEntityId,
4
- decisionEntityId,
5
- dirEntityId,
6
- fileEntityId,
7
- init_ops,
8
- init_types,
9
- issueEntityId,
10
- laneEntityId
11
- } from "./chunk-V3G7Q3QQ.js";
12
- import {
13
- __esm,
14
- __require
15
- } from "./chunk-2ESYSVXG.js";
16
-
17
- // src/vcs/decompose.ts
18
- import { dirname } from "path";
19
- function pickFacts(input) {
20
- if (!Array.isArray(input)) return [];
21
- return input.filter((item) => {
22
- const fact = item;
23
- return typeof fact.e === "string" && typeof fact.a === "string" && (typeof fact.v === "string" || typeof fact.v === "number" || typeof fact.v === "boolean");
24
- });
25
- }
26
- function pickLinks(input) {
27
- if (!Array.isArray(input)) return [];
28
- return input.filter((item) => {
29
- const link = item;
30
- return typeof link.e1 === "string" && typeof link.a === "string" && typeof link.e2 === "string";
31
- });
32
- }
33
- function decompose(op) {
34
- const result = {
35
- addFacts: [],
36
- addLinks: [],
37
- deleteFacts: [],
38
- deleteLinks: []
39
- };
40
- const vcs = op.vcs;
41
- if (!vcs) return result;
42
- if (vcs.issueId && vcs.oldIssueStatus) {
43
- result.deleteFacts.push({
44
- e: issueEntityId(vcs.issueId),
45
- a: "status",
46
- v: vcs.oldIssueStatus
47
- });
48
- }
49
- switch (op.kind) {
50
- case "vcs:fileAdd": {
51
- if (!vcs.filePath) break;
52
- const eid = fileEntityId(vcs.filePath);
53
- const dir = dirname(vcs.filePath);
54
- const did = dirEntityId(dir === "." ? "" : dir);
55
- result.addFacts.push(
56
- { e: eid, a: "type", v: "FileNode" },
57
- { e: eid, a: "path", v: vcs.filePath }
58
- );
59
- if (vcs.contentHash) {
60
- result.addFacts.push({ e: eid, a: "contentHash", v: vcs.contentHash });
61
- }
62
- if (vcs.size !== void 0) {
63
- result.addFacts.push({ e: eid, a: "size", v: vcs.size });
64
- }
65
- if (vcs.language) {
66
- result.addFacts.push({ e: eid, a: "language", v: vcs.language });
67
- }
68
- result.addFacts.push({ e: eid, a: "lastModified", v: op.timestamp });
69
- result.addFacts.push(
70
- { e: did, a: "type", v: "DirectoryNode" },
71
- { e: did, a: "path", v: dir === "." ? "" : dir }
72
- );
73
- result.addLinks.push({ e1: did, a: "contains", e2: eid });
74
- break;
75
- }
76
- case "vcs:fileModify": {
77
- if (!vcs.filePath) break;
78
- const eid = fileEntityId(vcs.filePath);
79
- if (vcs.oldContentHash) {
80
- result.deleteFacts.push({
81
- e: eid,
82
- a: "contentHash",
83
- v: vcs.oldContentHash
84
- });
85
- }
86
- if (vcs.contentHash) {
87
- result.addFacts.push({ e: eid, a: "contentHash", v: vcs.contentHash });
88
- }
89
- if (vcs.size !== void 0) {
90
- result.addFacts.push({ e: eid, a: "size", v: vcs.size });
91
- }
92
- result.addFacts.push({ e: eid, a: "lastModified", v: op.timestamp });
93
- break;
94
- }
95
- case "vcs:fileDelete": {
96
- if (!vcs.filePath) break;
97
- const eid = fileEntityId(vcs.filePath);
98
- const dir = dirname(vcs.filePath);
99
- const did = dirEntityId(dir === "." ? "" : dir);
100
- result.deleteFacts.push(
101
- { e: eid, a: "type", v: "FileNode" },
102
- { e: eid, a: "path", v: vcs.filePath }
103
- );
104
- if (vcs.contentHash) {
105
- result.deleteFacts.push({
106
- e: eid,
107
- a: "contentHash",
108
- v: vcs.contentHash
109
- });
110
- }
111
- result.deleteLinks.push({ e1: did, a: "contains", e2: eid });
112
- break;
113
- }
114
- case "vcs:fileRename": {
115
- if (!vcs.filePath || !vcs.oldFilePath) break;
116
- const eid = fileEntityId(vcs.oldFilePath);
117
- const oldDir = dirname(vcs.oldFilePath);
118
- const newDir = dirname(vcs.filePath);
119
- const oldDid = dirEntityId(oldDir === "." ? "" : oldDir);
120
- const newDid = dirEntityId(newDir === "." ? "" : newDir);
121
- result.deleteFacts.push({ e: eid, a: "path", v: vcs.oldFilePath });
122
- result.addFacts.push({ e: eid, a: "path", v: vcs.filePath });
123
- result.addFacts.push({ e: eid, a: "lastModified", v: op.timestamp });
124
- result.deleteLinks.push({ e1: oldDid, a: "contains", e2: eid });
125
- result.addFacts.push(
126
- { e: newDid, a: "type", v: "DirectoryNode" },
127
- { e: newDid, a: "path", v: newDir === "." ? "" : newDir }
128
- );
129
- result.addLinks.push({ e1: newDid, a: "contains", e2: eid });
130
- break;
131
- }
132
- case "vcs:branchCreate": {
133
- if (!vcs.branchName) break;
134
- const bid = `branch:${vcs.branchName}`;
135
- result.addFacts.push(
136
- { e: bid, a: "type", v: "Branch" },
137
- { e: bid, a: "name", v: vcs.branchName },
138
- { e: bid, a: "createdAt", v: op.timestamp },
139
- { e: bid, a: "createdBy", v: op.agentId }
140
- );
141
- if (vcs.targetOpHash) {
142
- result.addFacts.push({ e: bid, a: "headOpHash", v: vcs.targetOpHash });
143
- }
144
- if (vcs.baseBranch) {
145
- result.addLinks.push({
146
- e1: bid,
147
- a: "forkedFrom",
148
- e2: `branch:${vcs.baseBranch}`
149
- });
150
- }
151
- break;
152
- }
153
- case "vcs:branchDelete": {
154
- if (!vcs.branchName) break;
155
- const bid = `branch:${vcs.branchName}`;
156
- result.deleteFacts.push(
157
- { e: bid, a: "type", v: "Branch" },
158
- { e: bid, a: "name", v: vcs.branchName }
159
- );
160
- break;
161
- }
162
- case "vcs:branchAdvance": {
163
- if (!vcs.branchName || !vcs.targetOpHash) break;
164
- const bid = `branch:${vcs.branchName}`;
165
- result.addFacts.push({ e: bid, a: "headOpHash", v: vcs.targetOpHash });
166
- break;
167
- }
168
- case "vcs:milestoneCreate": {
169
- if (!vcs.milestoneId) break;
170
- const mid = vcs.milestoneId;
171
- result.addFacts.push(
172
- { e: mid, a: "type", v: "Milestone" },
173
- { e: mid, a: "createdAt", v: op.timestamp },
174
- { e: mid, a: "createdBy", v: op.agentId }
175
- );
176
- if (vcs.message) {
177
- result.addFacts.push({ e: mid, a: "message", v: vcs.message });
178
- }
179
- if (vcs.fromOpHash) {
180
- result.addFacts.push({ e: mid, a: "fromOpHash", v: vcs.fromOpHash });
181
- }
182
- if (vcs.toOpHash) {
183
- result.addFacts.push({ e: mid, a: "toOpHash", v: vcs.toOpHash });
184
- }
185
- break;
186
- }
187
- case "vcs:checkpointCreate": {
188
- const cid = `checkpoint:${op.hash}`;
189
- result.addFacts.push(
190
- { e: cid, a: "type", v: "Checkpoint" },
191
- { e: cid, a: "createdAt", v: op.timestamp },
192
- { e: cid, a: "atOpHash", v: op.hash }
193
- );
194
- if (vcs.trigger) {
195
- result.addFacts.push({ e: cid, a: "trigger", v: vcs.trigger });
196
- }
197
- break;
198
- }
199
- // ----- Issue tracking -----
200
- case "vcs:issueCreate": {
201
- if (!vcs.issueId) break;
202
- const eid = issueEntityId(vcs.issueId);
203
- result.addFacts.push(
204
- { e: eid, a: "type", v: "Issue" },
205
- { e: eid, a: "status", v: vcs.issueStatus ?? "backlog" },
206
- { e: eid, a: "createdAt", v: op.timestamp },
207
- { e: eid, a: "createdBy", v: op.agentId }
208
- );
209
- if (vcs.issueTitle) {
210
- result.addFacts.push({ e: eid, a: "title", v: vcs.issueTitle });
211
- }
212
- if (vcs.issueDescription) {
213
- result.addFacts.push({
214
- e: eid,
215
- a: "description",
216
- v: vcs.issueDescription
217
- });
218
- }
219
- if (vcs.issuePriority) {
220
- result.addFacts.push({ e: eid, a: "priority", v: vcs.issuePriority });
221
- }
222
- if (vcs.issueLabels && vcs.issueLabels.length > 0) {
223
- result.addFacts.push({
224
- e: eid,
225
- a: "labels",
226
- v: vcs.issueLabels.join(",")
227
- });
228
- }
229
- if (vcs.issueAssignee) {
230
- result.addFacts.push({ e: eid, a: "assignee", v: vcs.issueAssignee });
231
- }
232
- if (vcs.parentIssueId) {
233
- result.addLinks.push({
234
- e1: eid,
235
- a: "childOf",
236
- e2: issueEntityId(vcs.parentIssueId)
237
- });
238
- }
239
- break;
240
- }
241
- case "vcs:issueUpdate": {
242
- if (!vcs.issueId) break;
243
- const eid = issueEntityId(vcs.issueId);
244
- if (vcs.issueStatus) {
245
- result.addFacts.push({ e: eid, a: "status", v: vcs.issueStatus });
246
- }
247
- if (vcs.issuePriority) {
248
- result.addFacts.push({ e: eid, a: "priority", v: vcs.issuePriority });
249
- }
250
- if (vcs.issueLabels) {
251
- result.addFacts.push({
252
- e: eid,
253
- a: "labels",
254
- v: vcs.issueLabels.join(",")
255
- });
256
- }
257
- if (vcs.issueTitle) {
258
- result.addFacts.push({ e: eid, a: "title", v: vcs.issueTitle });
259
- }
260
- if (vcs.issueAssignee) {
261
- result.addFacts.push({ e: eid, a: "assignee", v: vcs.issueAssignee });
262
- }
263
- if (vcs.issueDescription !== void 0) {
264
- result.addFacts.push({
265
- e: eid,
266
- a: "description",
267
- v: vcs.issueDescription
268
- });
269
- }
270
- if (vcs.oldParentIssueId) {
271
- result.deleteLinks.push({
272
- e1: eid,
273
- a: "childOf",
274
- e2: issueEntityId(vcs.oldParentIssueId)
275
- });
276
- }
277
- if (vcs.parentIssueId) {
278
- result.addLinks.push({
279
- e1: eid,
280
- a: "childOf",
281
- e2: issueEntityId(vcs.parentIssueId)
282
- });
283
- }
284
- break;
285
- }
286
- case "vcs:issueStart": {
287
- if (!vcs.issueId) break;
288
- const eid = issueEntityId(vcs.issueId);
289
- result.addFacts.push(
290
- { e: eid, a: "status", v: "in_progress" },
291
- { e: eid, a: "startedAt", v: op.timestamp }
292
- );
293
- if (vcs.issueAssignee) {
294
- result.addFacts.push({ e: eid, a: "assignee", v: vcs.issueAssignee });
295
- }
296
- if (vcs.branchName) {
297
- result.addLinks.push({
298
- e1: eid,
299
- a: "trackedOn",
300
- e2: `branch:${vcs.branchName}`
301
- });
302
- }
303
- break;
304
- }
305
- case "vcs:issuePause": {
306
- if (!vcs.issueId) break;
307
- const eid = issueEntityId(vcs.issueId);
308
- result.addFacts.push(
309
- { e: eid, a: "status", v: "paused" },
310
- { e: eid, a: "pausedAt", v: op.timestamp }
311
- );
312
- if (vcs.pauseNote) {
313
- result.addFacts.push({ e: eid, a: "pauseNote", v: vcs.pauseNote });
314
- }
315
- break;
316
- }
317
- case "vcs:issueResume": {
318
- if (!vcs.issueId) break;
319
- const eid = issueEntityId(vcs.issueId);
320
- result.addFacts.push(
321
- { e: eid, a: "status", v: "in_progress" },
322
- { e: eid, a: "resumedAt", v: op.timestamp },
323
- { e: eid, a: "pauseNote", v: "" }
324
- );
325
- break;
326
- }
327
- case "vcs:issueClose": {
328
- if (!vcs.issueId) break;
329
- const eid = issueEntityId(vcs.issueId);
330
- result.addFacts.push(
331
- { e: eid, a: "status", v: "closed" },
332
- { e: eid, a: "closedAt", v: op.timestamp }
333
- );
334
- break;
335
- }
336
- case "vcs:issueReopen": {
337
- if (!vcs.issueId) break;
338
- const eid = issueEntityId(vcs.issueId);
339
- result.addFacts.push({ e: eid, a: "status", v: "queue" });
340
- break;
341
- }
342
- case "vcs:criterionAdd": {
343
- if (!vcs.criterionId || !vcs.issueId) break;
344
- const ceid = vcs.criterionId;
345
- result.addFacts.push(
346
- { e: ceid, a: "type", v: "Criterion" },
347
- { e: ceid, a: "status", v: "pending" },
348
- { e: ceid, a: "createdAt", v: op.timestamp }
349
- );
350
- if (vcs.criterionDescription) {
351
- result.addFacts.push({
352
- e: ceid,
353
- a: "description",
354
- v: vcs.criterionDescription
355
- });
356
- }
357
- if (vcs.criterionCommand) {
358
- result.addFacts.push({
359
- e: ceid,
360
- a: "command",
361
- v: vcs.criterionCommand
362
- });
363
- }
364
- result.addLinks.push({
365
- e1: ceid,
366
- a: "criterionOf",
367
- e2: issueEntityId(vcs.issueId)
368
- });
369
- break;
370
- }
371
- case "vcs:criterionUpdate": {
372
- if (!vcs.criterionId) break;
373
- const ceid = vcs.criterionId;
374
- if (vcs.criterionStatus) {
375
- result.addFacts.push({ e: ceid, a: "status", v: vcs.criterionStatus });
376
- }
377
- if (vcs.criterionOutput) {
378
- result.addFacts.push({
379
- e: ceid,
380
- a: "lastOutput",
381
- v: vcs.criterionOutput
382
- });
383
- }
384
- result.addFacts.push({ e: ceid, a: "lastRunAt", v: op.timestamp });
385
- break;
386
- }
387
- // ----- Issue blocking -----
388
- case "vcs:issueBlock": {
389
- if (!vcs.issueId || !vcs.blockedByIssueId) break;
390
- const eid = issueEntityId(vcs.issueId);
391
- const blockerEid = issueEntityId(vcs.blockedByIssueId);
392
- result.addLinks.push({ e1: eid, a: "blockedBy", e2: blockerEid });
393
- break;
394
- }
395
- case "vcs:issueUnblock": {
396
- if (!vcs.issueId || !vcs.blockedByIssueId) break;
397
- const eid = issueEntityId(vcs.issueId);
398
- const blockerEid = issueEntityId(vcs.blockedByIssueId);
399
- result.deleteLinks.push({ e1: eid, a: "blockedBy", e2: blockerEid });
400
- break;
401
- }
402
- // ----- Decision traces -----
403
- case "vcs:decisionRecord": {
404
- if (!vcs.decisionId) break;
405
- const did = decisionEntityId(vcs.decisionId);
406
- result.addFacts.push(
407
- { e: did, a: "type", v: "Decision" },
408
- { e: did, a: "createdAt", v: op.timestamp },
409
- { e: did, a: "createdBy", v: op.agentId }
410
- );
411
- if (vcs.decisionToolName) {
412
- result.addFacts.push({
413
- e: did,
414
- a: "toolName",
415
- v: vcs.decisionToolName
416
- });
417
- }
418
- if (vcs.decisionToolInput) {
419
- result.addFacts.push({
420
- e: did,
421
- a: "toolInput",
422
- v: vcs.decisionToolInput
423
- });
424
- }
425
- if (vcs.decisionToolOutput) {
426
- result.addFacts.push({
427
- e: did,
428
- a: "outputSummary",
429
- v: vcs.decisionToolOutput
430
- });
431
- }
432
- if (vcs.decisionContext) {
433
- result.addFacts.push({ e: did, a: "context", v: vcs.decisionContext });
434
- }
435
- if (vcs.decisionRationale) {
436
- result.addFacts.push({
437
- e: did,
438
- a: "rationale",
439
- v: vcs.decisionRationale
440
- });
441
- }
442
- if (vcs.decisionAlternatives) {
443
- result.addFacts.push({
444
- e: did,
445
- a: "alternatives",
446
- v: vcs.decisionAlternatives
447
- });
448
- }
449
- break;
450
- }
451
- case "vcs:laneCreate": {
452
- if (!vcs.laneId) break;
453
- const lid = laneEntityId(vcs.laneId);
454
- result.addFacts.push(
455
- { e: lid, a: "type", v: "AgentLane" },
456
- { e: lid, a: "status", v: "active" },
457
- { e: lid, a: "createdAt", v: op.timestamp },
458
- { e: lid, a: "createdBy", v: op.agentId }
459
- );
460
- if (vcs.baseBranch) {
461
- result.addFacts.push({ e: lid, a: "baseBranch", v: vcs.baseBranch });
462
- }
463
- if (vcs.baseOpHash) {
464
- result.addFacts.push({ e: lid, a: "baseOpHash", v: vcs.baseOpHash });
465
- }
466
- if (vcs.targetBranch) {
467
- result.addFacts.push({ e: lid, a: "targetBranch", v: vcs.targetBranch });
468
- }
469
- if (vcs.baseOpHash) {
470
- result.addFacts.push({ e: lid, a: "headOpHash", v: vcs.baseOpHash });
471
- }
472
- if (vcs.issueId) {
473
- result.addFacts.push({ e: lid, a: "issueId", v: vcs.issueId });
474
- }
475
- if (vcs.sessionId) {
476
- result.addFacts.push({ e: lid, a: "sessionId", v: vcs.sessionId });
477
- }
478
- if (vcs.parentLaneId) {
479
- result.addFacts.push({ e: lid, a: "parentLaneId", v: vcs.parentLaneId });
480
- result.addLinks.push({
481
- e1: lid,
482
- a: "forkedFrom",
483
- e2: laneEntityId(vcs.parentLaneId)
484
- });
485
- }
486
- if (vcs.forkKind) {
487
- result.addFacts.push({ e: lid, a: "forkKind", v: vcs.forkKind });
488
- }
489
- if (vcs.virtualBaseOpHash) {
490
- result.addFacts.push({
491
- e: lid,
492
- a: "virtualBaseOpHash",
493
- v: vcs.virtualBaseOpHash
494
- });
495
- }
496
- break;
497
- }
498
- case "vcs:laneDrop": {
499
- if (!vcs.laneId) break;
500
- const lid = laneEntityId(vcs.laneId);
501
- if (vcs.laneStatus) {
502
- result.deleteFacts.push({ e: lid, a: "status", v: "active" });
503
- result.addFacts.push({ e: lid, a: "status", v: vcs.laneStatus });
504
- }
505
- break;
506
- }
507
- case "vcs:lanePromoteStart": {
508
- if (!vcs.laneId) break;
509
- const lid = laneEntityId(vcs.laneId);
510
- result.deleteFacts.push({ e: lid, a: "status", v: "active" });
511
- result.addFacts.push({ e: lid, a: "status", v: "promoting" });
512
- break;
513
- }
514
- case "vcs:lanePromoteComplete": {
515
- if (!vcs.laneId) break;
516
- const lid = laneEntityId(vcs.laneId);
517
- result.deleteFacts.push({ e: lid, a: "status", v: "promoting" });
518
- result.addFacts.push({ e: lid, a: "status", v: "promoted" });
519
- if (vcs.targetBranch) {
520
- result.addFacts.push({ e: lid, a: "promotedToBranch", v: vcs.targetBranch });
521
- }
522
- break;
523
- }
524
- case "vcs:lanePromoteAbort": {
525
- if (!vcs.laneId) break;
526
- const lid = laneEntityId(vcs.laneId);
527
- result.deleteFacts.push({ e: lid, a: "status", v: "promoting" });
528
- result.addFacts.push({ e: lid, a: "status", v: "active" });
529
- break;
530
- }
531
- // ----- EAV store (CMS / knowledge graph) -----
532
- case "vcs:storeAssert": {
533
- result.addFacts.push(...pickFacts(vcs.facts));
534
- break;
535
- }
536
- case "vcs:storeRetract": {
537
- result.deleteFacts.push(...pickFacts(vcs.facts));
538
- break;
539
- }
540
- case "vcs:storeLink": {
541
- result.addLinks.push(...pickLinks(vcs.links));
542
- break;
543
- }
544
- case "vcs:storeUnlink": {
545
- result.deleteLinks.push(...pickLinks(vcs.links));
546
- break;
547
- }
548
- }
549
- return result;
550
- }
551
- var init_decompose = __esm({
552
- "src/vcs/decompose.ts"() {
553
- "use strict";
554
- init_types();
555
- }
556
- });
557
-
558
- // src/vcs/blob-store.ts
559
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
560
- import { join } from "path";
561
- var BlobStore;
562
- var init_blob_store = __esm({
563
- "src/vcs/blob-store.ts"() {
564
- "use strict";
565
- BlobStore = class {
566
- blobDir;
567
- constructor(trellisDir) {
568
- this.blobDir = join(trellisDir, "blobs");
569
- if (!existsSync(this.blobDir)) {
570
- mkdirSync(this.blobDir, { recursive: true });
571
- }
572
- }
573
- /**
574
- * Store content and return its SHA-256 hash.
575
- * Idempotent — storing the same content twice is a no-op.
576
- */
577
- async put(content) {
578
- const hash = await this.hash(content);
579
- const blobPath = join(this.blobDir, hash);
580
- if (!existsSync(blobPath)) {
581
- writeFileSync(blobPath, content);
582
- }
583
- return hash;
584
- }
585
- /**
586
- * Synchronous put — uses Bun's sync crypto if available.
587
- */
588
- putSync(content) {
589
- const hash = this.hashSync(content);
590
- const blobPath = join(this.blobDir, hash);
591
- if (!existsSync(blobPath)) {
592
- writeFileSync(blobPath, content);
593
- }
594
- return hash;
595
- }
596
- /**
597
- * Retrieve content by hash. Returns null if not found.
598
- */
599
- get(hash) {
600
- const blobPath = join(this.blobDir, hash);
601
- if (!existsSync(blobPath)) {
602
- return null;
603
- }
604
- return readFileSync(blobPath);
605
- }
606
- /**
607
- * Check if a blob exists.
608
- */
609
- has(hash) {
610
- return existsSync(join(this.blobDir, hash));
611
- }
612
- /**
613
- * Compute SHA-256 hash of content (async).
614
- */
615
- async hash(content) {
616
- const hashBuffer = await crypto.subtle.digest(
617
- "SHA-256",
618
- content
619
- );
620
- return this.hexFromBuffer(hashBuffer);
621
- }
622
- /**
623
- * Compute SHA-256 hash of content (sync).
624
- * Uses Bun.CryptoHasher when running under Bun, else node:crypto.
625
- */
626
- hashSync(content) {
627
- const { createHash } = __require("crypto");
628
- return createHash("sha256").update(content).digest("hex");
629
- }
630
- /**
631
- * Returns the number of blobs stored.
632
- */
633
- count() {
634
- try {
635
- const { readdirSync: readdirSync2 } = __require("fs");
636
- return readdirSync2(this.blobDir).length;
637
- } catch {
638
- return 0;
639
- }
640
- }
641
- /**
642
- * Returns the total size of all blobs in bytes.
643
- */
644
- totalSize() {
645
- try {
646
- const { readdirSync: readdirSync2, statSync } = __require("fs");
647
- const files = readdirSync2(this.blobDir);
648
- return files.reduce((sum, f) => {
649
- try {
650
- return sum + statSync(join(this.blobDir, f)).size;
651
- } catch {
652
- return sum;
653
- }
654
- }, 0);
655
- } catch {
656
- return 0;
657
- }
658
- }
659
- hexFromBuffer(buffer) {
660
- return Array.from(new Uint8Array(buffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
661
- }
662
- };
663
- }
664
- });
665
-
666
- // src/vcs/branch.ts
667
- import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
668
- import { join as join2 } from "path";
669
- function shouldAdvanceBranchHead(kind) {
670
- return !BRANCH_ADVANCE_SKIP_KINDS.has(kind);
671
- }
672
- function getBranchHeadOpHash(ctx, branchName) {
673
- const facts = ctx.store.getFactsByEntity(`branch:${branchName}`).filter((f) => f.a === "headOpHash");
674
- return facts[facts.length - 1]?.v;
675
- }
676
- async function createBranch(ctx, name, currentBranch) {
677
- const existing = ctx.store.getFactsByAttribute("type").filter((f) => f.v === "Branch" && f.e === `branch:${name}`);
678
- if (existing.length > 0) {
679
- throw new Error(`Branch '${name}' already exists`);
680
- }
681
- const op = await createVcsOp("vcs:branchCreate", {
682
- agentId: ctx.agentId,
683
- previousHash: ctx.getLastOp()?.hash,
684
- vcs: {
685
- branchName: name,
686
- baseBranch: currentBranch,
687
- targetOpHash: ctx.getLastOp()?.hash
688
- }
689
- });
690
- await ctx.applyOp(op);
691
- return op;
692
- }
693
- function switchBranch(ctx, name) {
694
- const branchFacts = ctx.store.getFactsByEntity(`branch:${name}`).filter((f) => f.a === "type" && f.v === "Branch");
695
- if (branchFacts.length === 0) {
696
- throw new Error(`Branch '${name}' does not exist`);
697
- }
698
- }
699
- function listBranches(ctx, currentBranch) {
700
- const branchFacts = ctx.store.getFactsByAttribute("type").filter((f) => f.v === "Branch");
701
- return branchFacts.map((f) => {
702
- const nameFact = ctx.store.getFactsByEntity(f.e).find((ef) => ef.a === "name");
703
- const createdFact = ctx.store.getFactsByEntity(f.e).find((ef) => ef.a === "createdAt");
704
- const name = nameFact?.v ?? f.e.replace("branch:", "");
705
- return {
706
- name,
707
- isCurrent: name === currentBranch,
708
- createdAt: createdFact?.v
709
- };
710
- });
711
- }
712
- async function deleteBranch(ctx, name, currentBranch) {
713
- if (name === currentBranch) {
714
- throw new Error(`Cannot delete the current branch '${name}'`);
715
- }
716
- const branchFacts = ctx.store.getFactsByEntity(`branch:${name}`).filter((f) => f.a === "type" && f.v === "Branch");
717
- if (branchFacts.length === 0) {
718
- throw new Error(`Branch '${name}' does not exist`);
719
- }
720
- const op = await createVcsOp("vcs:branchDelete", {
721
- agentId: ctx.agentId,
722
- previousHash: ctx.getLastOp()?.hash,
723
- vcs: { branchName: name }
724
- });
725
- await ctx.applyOp(op);
726
- return op;
727
- }
728
- function saveBranchState(rootPath, state) {
729
- const statePath = join2(rootPath, ".trellis", "state.json");
730
- writeFileSync2(statePath, JSON.stringify(state));
731
- }
732
- function loadBranchState(rootPath) {
733
- const statePath = join2(rootPath, ".trellis", "state.json");
734
- if (existsSync2(statePath)) {
735
- try {
736
- const raw = readFileSync2(statePath, "utf-8");
737
- const state = JSON.parse(raw);
738
- if (state.currentBranch) {
739
- return {
740
- currentBranch: state.currentBranch,
741
- activeLaneId: state.activeLaneId
742
- };
743
- }
744
- } catch {
745
- }
746
- }
747
- return { currentBranch: "main" };
748
- }
749
- var BRANCH_ADVANCE_SKIP_KINDS;
750
- var init_branch = __esm({
751
- "src/vcs/branch.ts"() {
752
- "use strict";
753
- init_ops();
754
- BRANCH_ADVANCE_SKIP_KINDS = /* @__PURE__ */ new Set([
755
- "vcs:branchAdvance",
756
- "vcs:branchCreate",
757
- "vcs:branchDelete",
758
- "vcs:checkpointCreate"
759
- ]);
760
- }
761
- });
762
-
763
- // src/vcs/milestone.ts
764
- async function createMilestone(ctx, message, opts) {
765
- const ops = ctx.readAllOps();
766
- const toOpHash = opts?.toOpHash ?? ops[ops.length - 1]?.hash;
767
- let fromOpHash = opts?.fromOpHash;
768
- if (!fromOpHash) {
769
- const milestones = ops.filter((o) => o.kind === "vcs:milestoneCreate");
770
- if (milestones.length > 0) {
771
- const lastMilestone = milestones[milestones.length - 1];
772
- fromOpHash = lastMilestone.vcs?.toOpHash ?? lastMilestone.hash;
773
- } else {
774
- fromOpHash = ops[0]?.hash;
775
- }
776
- }
777
- const idBase = `${message}:${Date.now()}`;
778
- const msgUint8 = new TextEncoder().encode(idBase);
779
- const hashBuffer = await crypto.subtle.digest("SHA-256", msgUint8);
780
- const hashArray = Array.from(new Uint8Array(hashBuffer));
781
- const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
782
- const milestoneId = `milestone:${hashHex.slice(0, 12)}`;
783
- const fromIdx = ops.findIndex((o) => o.hash === fromOpHash);
784
- const toIdx = ops.findIndex((o) => o.hash === toOpHash);
785
- const rangeOps = fromIdx >= 0 && toIdx >= 0 ? ops.slice(fromIdx, toIdx + 1) : ops;
786
- const affectedFiles = [
787
- ...new Set(
788
- rangeOps.filter((o) => o.vcs?.filePath).map((o) => o.vcs.filePath)
789
- )
790
- ];
791
- const op = await createVcsOp("vcs:milestoneCreate", {
792
- agentId: ctx.agentId,
793
- previousHash: ctx.getLastOp()?.hash,
794
- vcs: {
795
- milestoneId,
796
- message,
797
- fromOpHash,
798
- toOpHash
799
- }
800
- });
801
- await ctx.applyOp(op);
802
- for (const file of affectedFiles) {
803
- ctx.store.addFacts([{ e: milestoneId, a: "affectsFile", v: file }]);
804
- }
805
- return op;
806
- }
807
- function listMilestones(ctx) {
808
- const milestoneFacts = ctx.store.getFactsByAttribute("type").filter((f) => f.v === "Milestone");
809
- return milestoneFacts.map((f) => {
810
- const facts = ctx.store.getFactsByEntity(f.e);
811
- const get = (attr) => facts.find((ef) => ef.a === attr)?.v;
812
- const affectedFiles = facts.filter((ef) => ef.a === "affectsFile").map((ef) => ef.v);
813
- return {
814
- id: f.e,
815
- message: get("message"),
816
- createdAt: get("createdAt"),
817
- createdBy: get("createdBy"),
818
- fromOpHash: get("fromOpHash"),
819
- toOpHash: get("toOpHash"),
820
- affectedFiles
821
- };
822
- });
823
- }
824
- var init_milestone = __esm({
825
- "src/vcs/milestone.ts"() {
826
- "use strict";
827
- init_ops();
828
- }
829
- });
830
-
831
- // src/vcs/checkpoint.ts
832
- async function createCheckpoint(ctx, trigger = "manual") {
833
- const op = await createVcsOp("vcs:checkpointCreate", {
834
- agentId: ctx.agentId,
835
- previousHash: ctx.getLastOp()?.hash,
836
- vcs: { trigger }
837
- });
838
- await ctx.applyOp(op);
839
- return op;
840
- }
841
- function listCheckpoints(ctx) {
842
- const cpFacts = ctx.store.getFactsByAttribute("type").filter((f) => f.v === "Checkpoint");
843
- return cpFacts.map((f) => {
844
- const facts = ctx.store.getFactsByEntity(f.e);
845
- const get = (attr) => facts.find((ef) => ef.a === attr)?.v;
846
- return {
847
- id: f.e,
848
- createdAt: get("createdAt"),
849
- trigger: get("trigger"),
850
- atOpHash: get("atOpHash")
851
- };
852
- });
853
- }
854
- var init_checkpoint = __esm({
855
- "src/vcs/checkpoint.ts"() {
856
- "use strict";
857
- init_ops();
858
- }
859
- });
860
-
861
- // src/vcs/diff.ts
862
- function diffFileStates(stateA, stateB, blobStore) {
863
- const diffs = [];
864
- for (const [path, bState] of stateB) {
865
- if (bState.deleted) continue;
866
- const aState = stateA.get(path);
867
- if (!aState || aState.deleted) {
868
- diffs.push({
869
- kind: "fileAdded",
870
- path,
871
- newContentHash: bState.contentHash
872
- });
873
- } else if (aState.contentHash !== bState.contentHash) {
874
- const diff = {
875
- kind: "fileModified",
876
- path,
877
- oldContentHash: aState.contentHash,
878
- newContentHash: bState.contentHash
879
- };
880
- if (blobStore && aState.contentHash && bState.contentHash) {
881
- const oldContent = blobStore.get(aState.contentHash);
882
- const newContent = blobStore.get(bState.contentHash);
883
- if (oldContent && newContent) {
884
- diff.unifiedDiff = generateUnifiedDiff(
885
- path,
886
- oldContent.toString("utf-8"),
887
- newContent.toString("utf-8")
888
- );
889
- }
890
- }
891
- diffs.push(diff);
892
- }
893
- }
894
- for (const [path, aState] of stateA) {
895
- if (aState.deleted) continue;
896
- const bState = stateB.get(path);
897
- if (!bState || bState.deleted) {
898
- diffs.push({
899
- kind: "fileDeleted",
900
- path,
901
- oldContentHash: aState.contentHash
902
- });
903
- }
904
- }
905
- const stats = {
906
- added: diffs.filter((d) => d.kind === "fileAdded").length,
907
- modified: diffs.filter((d) => d.kind === "fileModified").length,
908
- removed: diffs.filter((d) => d.kind === "fileDeleted").length,
909
- renamed: diffs.filter((d) => d.kind === "fileRenamed").length
910
- };
911
- return {
912
- diffs,
913
- filesChanged: diffs.map((d) => d.path),
914
- stats
915
- };
916
- }
917
- function buildFileStateAtOp(ops, atOpHash) {
918
- const state = /* @__PURE__ */ new Map();
919
- for (const op of ops) {
920
- if (op.vcs?.filePath) {
921
- switch (op.kind) {
922
- case "vcs:fileAdd":
923
- case "vcs:fileModify":
924
- state.set(op.vcs.filePath, { contentHash: op.vcs.contentHash });
925
- break;
926
- case "vcs:fileDelete":
927
- state.set(op.vcs.filePath, { deleted: true });
928
- break;
929
- case "vcs:fileRename":
930
- if (op.vcs.oldFilePath) {
931
- state.set(op.vcs.oldFilePath, { deleted: true });
932
- }
933
- state.set(op.vcs.filePath, { contentHash: op.vcs.contentHash });
934
- break;
935
- }
936
- }
937
- if (atOpHash && op.hash === atOpHash) break;
938
- }
939
- return state;
940
- }
941
- function diffOpRange(ops, fromHash, toHash, blobStore) {
942
- const stateA = buildFileStateAtOp(ops, fromHash);
943
- const stateB = buildFileStateAtOp(ops, toHash);
944
- return diffFileStates(stateA, stateB, blobStore);
945
- }
946
- function generateUnifiedDiff(filePath, oldText, newText, contextLines = 3) {
947
- const oldLines = oldText.split("\n");
948
- const newLines = newText.split("\n");
949
- const edits = myersDiff(oldLines, newLines);
950
- const hunks = buildHunks(edits, contextLines);
951
- if (hunks.length === 0) return "";
952
- const lines = [`--- a/${filePath}`, `+++ b/${filePath}`];
953
- for (const hunk of hunks) {
954
- lines.push(
955
- `@@ -${hunk.oldStart},${hunk.oldCount} +${hunk.newStart},${hunk.newCount} @@`
956
- );
957
- for (const edit of hunk.edits) {
958
- switch (edit.kind) {
959
- case "equal":
960
- lines.push(` ${edit.line}`);
961
- break;
962
- case "delete":
963
- lines.push(`-${edit.line}`);
964
- break;
965
- case "insert":
966
- lines.push(`+${edit.line}`);
967
- break;
968
- }
969
- }
970
- }
971
- return lines.join("\n");
972
- }
973
- function myersDiff(oldLines, newLines) {
974
- const n = oldLines.length;
975
- const m = newLines.length;
976
- if (n === 0 && m === 0) return [];
977
- if (n === 0)
978
- return newLines.map((line) => ({ kind: "insert", line }));
979
- if (m === 0)
980
- return oldLines.map((line) => ({ kind: "delete", line }));
981
- const max = n + m;
982
- const size = 2 * max + 1;
983
- const v = new Int32Array(size);
984
- const trace = [];
985
- const off = max;
986
- outer: for (let d = 0; d <= max; d++) {
987
- trace.push(v.slice());
988
- for (let k = -d; k <= d; k += 2) {
989
- let x2;
990
- if (k === -d || k !== d && v[k - 1 + off] < v[k + 1 + off]) {
991
- x2 = v[k + 1 + off];
992
- } else {
993
- x2 = v[k - 1 + off] + 1;
994
- }
995
- let y2 = x2 - k;
996
- while (x2 < n && y2 < m && oldLines[x2] === newLines[y2]) {
997
- x2++;
998
- y2++;
999
- }
1000
- v[k + off] = x2;
1001
- if (x2 >= n && y2 >= m) break outer;
1002
- }
1003
- }
1004
- let x = n;
1005
- let y = m;
1006
- const edits = [];
1007
- for (let d = trace.length - 1; d >= 0; d--) {
1008
- const tv = trace[d];
1009
- const k = x - y;
1010
- let prevK;
1011
- if (d === 0) {
1012
- while (x > 0 && y > 0) {
1013
- x--;
1014
- y--;
1015
- edits.push({ kind: "equal", line: oldLines[x] });
1016
- }
1017
- break;
1018
- }
1019
- if (k === -d || k !== d && tv[k - 1 + off] < tv[k + 1 + off]) {
1020
- prevK = k + 1;
1021
- } else {
1022
- prevK = k - 1;
1023
- }
1024
- const prevX = tv[prevK + off];
1025
- const prevY = prevX - prevK;
1026
- while (x > prevX && y > prevY) {
1027
- x--;
1028
- y--;
1029
- edits.push({ kind: "equal", line: oldLines[x] });
1030
- }
1031
- if (prevK === k + 1) {
1032
- y--;
1033
- edits.push({ kind: "insert", line: newLines[y] });
1034
- } else {
1035
- x--;
1036
- edits.push({ kind: "delete", line: oldLines[x] });
1037
- }
1038
- }
1039
- edits.reverse();
1040
- return edits;
1041
- }
1042
- function buildHunks(edits, contextLines) {
1043
- if (edits.length === 0) return [];
1044
- const changeIndices = [];
1045
- for (let i = 0; i < edits.length; i++) {
1046
- if (edits[i].kind !== "equal") {
1047
- changeIndices.push(i);
1048
- }
1049
- }
1050
- if (changeIndices.length === 0) return [];
1051
- const hunks = [];
1052
- let hunkStart = Math.max(0, changeIndices[0] - contextLines);
1053
- let hunkEnd = Math.min(edits.length - 1, changeIndices[0] + contextLines);
1054
- for (let i = 1; i < changeIndices.length; i++) {
1055
- const changeStart = changeIndices[i] - contextLines;
1056
- const changeEnd = Math.min(
1057
- edits.length - 1,
1058
- changeIndices[i] + contextLines
1059
- );
1060
- if (changeStart <= hunkEnd + 1) {
1061
- hunkEnd = changeEnd;
1062
- } else {
1063
- hunks.push(createHunk(edits, hunkStart, hunkEnd));
1064
- hunkStart = changeStart;
1065
- hunkEnd = changeEnd;
1066
- }
1067
- }
1068
- hunks.push(createHunk(edits, hunkStart, hunkEnd));
1069
- return hunks;
1070
- }
1071
- function createHunk(edits, start, end) {
1072
- const hunkEdits = edits.slice(start, end + 1);
1073
- let oldLine = 1;
1074
- let newLine = 1;
1075
- for (let i = 0; i < start; i++) {
1076
- if (edits[i].kind === "equal" || edits[i].kind === "delete") oldLine++;
1077
- if (edits[i].kind === "equal" || edits[i].kind === "insert") newLine++;
1078
- }
1079
- let oldCount = 0;
1080
- let newCount = 0;
1081
- for (const edit of hunkEdits) {
1082
- if (edit.kind === "equal" || edit.kind === "delete") oldCount++;
1083
- if (edit.kind === "equal" || edit.kind === "insert") newCount++;
1084
- }
1085
- return {
1086
- oldStart: oldLine,
1087
- oldCount,
1088
- newStart: newLine,
1089
- newCount,
1090
- edits: hunkEdits
1091
- };
1092
- }
1093
- var init_diff = __esm({
1094
- "src/vcs/diff.ts"() {
1095
- "use strict";
1096
- }
1097
- });
1098
-
1099
- // src/vcs/merge.ts
1100
- function threeWayMerge(base, ours, theirs, blobStore) {
1101
- const mergedFiles = /* @__PURE__ */ new Map();
1102
- const conflicts = [];
1103
- const allPaths = /* @__PURE__ */ new Set();
1104
- for (const [p, s] of base) if (!s.deleted) allPaths.add(p);
1105
- for (const [p, s] of ours) if (!s.deleted) allPaths.add(p);
1106
- for (const [p, s] of theirs) if (!s.deleted) allPaths.add(p);
1107
- for (const [p, s] of ours) if (s.deleted) allPaths.add(p);
1108
- for (const [p, s] of theirs) if (s.deleted) allPaths.add(p);
1109
- for (const path of allPaths) {
1110
- const b = base.get(path);
1111
- const o = ours.get(path);
1112
- const t = theirs.get(path);
1113
- const baseExists = b && !b.deleted;
1114
- const oursExists = o && !o.deleted;
1115
- const theirsExists = t && !t.deleted;
1116
- const baseHash = baseExists ? b.contentHash : void 0;
1117
- const oursHash = oursExists ? o.contentHash : void 0;
1118
- const theirsHash = theirsExists ? t.contentHash : void 0;
1119
- if (oursHash === theirsHash) {
1120
- continue;
1121
- }
1122
- if (theirsHash === baseHash && oursHash !== baseHash) {
1123
- if (!oursExists) {
1124
- mergedFiles.set(path, null);
1125
- }
1126
- continue;
1127
- }
1128
- if (oursHash === baseHash && theirsHash !== baseHash) {
1129
- if (!theirsExists) {
1130
- mergedFiles.set(path, null);
1131
- } else if (theirsHash && blobStore) {
1132
- const content = blobStore.get(theirsHash);
1133
- if (content) {
1134
- mergedFiles.set(path, content.toString("utf-8"));
1135
- }
1136
- }
1137
- continue;
1138
- }
1139
- if (!baseExists && oursExists && theirsExists) {
1140
- if (oursHash === theirsHash) {
1141
- continue;
1142
- }
1143
- const oursContent = oursHash && blobStore ? blobStore.get(oursHash)?.toString("utf-8") : void 0;
1144
- const theirsContent = theirsHash && blobStore ? blobStore.get(theirsHash)?.toString("utf-8") : void 0;
1145
- if (oursContent !== void 0 && theirsContent !== void 0) {
1146
- const textResult = threeWayTextMerge("", oursContent, theirsContent);
1147
- if (textResult.clean) {
1148
- mergedFiles.set(path, textResult.merged);
1149
- continue;
1150
- }
1151
- conflicts.push({
1152
- path,
1153
- kind: "add-add",
1154
- ours: oursContent,
1155
- theirs: theirsContent,
1156
- mergedWithMarkers: textResult.merged
1157
- });
1158
- } else {
1159
- conflicts.push({ path, kind: "add-add", ours: oursContent, theirs: theirsContent });
1160
- }
1161
- continue;
1162
- }
1163
- if (oursExists && !theirsExists) {
1164
- conflicts.push({
1165
- path,
1166
- kind: "modify-delete",
1167
- ours: oursHash && blobStore ? blobStore.get(oursHash)?.toString("utf-8") : void 0
1168
- });
1169
- continue;
1170
- }
1171
- if (!oursExists && theirsExists) {
1172
- conflicts.push({
1173
- path,
1174
- kind: "modify-delete",
1175
- theirs: theirsHash && blobStore ? blobStore.get(theirsHash)?.toString("utf-8") : void 0
1176
- });
1177
- continue;
1178
- }
1179
- if (oursExists && theirsExists && oursHash !== theirsHash) {
1180
- const baseContent = baseHash && blobStore ? blobStore.get(baseHash)?.toString("utf-8") : void 0;
1181
- const oursContent = oursHash && blobStore ? blobStore.get(oursHash)?.toString("utf-8") : void 0;
1182
- const theirsContent = theirsHash && blobStore ? blobStore.get(theirsHash)?.toString("utf-8") : void 0;
1183
- if (baseContent !== void 0 && oursContent !== void 0 && theirsContent !== void 0) {
1184
- const textResult = threeWayTextMerge(baseContent, oursContent, theirsContent);
1185
- if (textResult.clean) {
1186
- mergedFiles.set(path, textResult.merged);
1187
- } else {
1188
- conflicts.push({
1189
- path,
1190
- kind: "modify-modify",
1191
- base: baseContent,
1192
- ours: oursContent,
1193
- theirs: theirsContent,
1194
- mergedWithMarkers: textResult.merged
1195
- });
1196
- }
1197
- } else {
1198
- conflicts.push({
1199
- path,
1200
- kind: "modify-modify",
1201
- base: baseContent,
1202
- ours: oursContent,
1203
- theirs: theirsContent
1204
- });
1205
- }
1206
- continue;
1207
- }
1208
- }
1209
- const added = [...mergedFiles.values()].filter((v) => v !== null).length;
1210
- const deleted = [...mergedFiles.values()].filter((v) => v === null).length;
1211
- return {
1212
- clean: conflicts.length === 0,
1213
- mergedFiles,
1214
- conflicts,
1215
- stats: {
1216
- added,
1217
- modified: added,
1218
- // in a merge context, additions from theirs are "modified"
1219
- deleted,
1220
- conflicted: conflicts.length
1221
- }
1222
- };
1223
- }
1224
- function threeWayTextMerge(baseText, oursText, theirsText) {
1225
- const baseLines = baseText.split("\n");
1226
- const oursLines = oursText.split("\n");
1227
- const theirsLines = theirsText.split("\n");
1228
- const oursChanges = computeLineChanges(baseLines, oursLines);
1229
- const theirsChanges = computeLineChanges(baseLines, theirsLines);
1230
- const result = [];
1231
- let clean = true;
1232
- let baseIdx = 0;
1233
- let oursIdx = 0;
1234
- let theirsIdx = 0;
1235
- while (baseIdx < baseLines.length || oursIdx < oursLines.length || theirsIdx < theirsLines.length) {
1236
- const oursChange = oursChanges.get(baseIdx);
1237
- const theirsChange = theirsChanges.get(baseIdx);
1238
- if (baseIdx >= baseLines.length) {
1239
- while (oursIdx < oursLines.length) {
1240
- result.push(oursLines[oursIdx++]);
1241
- }
1242
- while (theirsIdx < theirsLines.length) {
1243
- result.push(theirsLines[theirsIdx++]);
1244
- }
1245
- break;
1246
- }
1247
- if (!oursChange && !theirsChange) {
1248
- result.push(baseLines[baseIdx]);
1249
- baseIdx++;
1250
- oursIdx++;
1251
- theirsIdx++;
1252
- } else if (oursChange && !theirsChange) {
1253
- applyChange(oursChange, result);
1254
- baseIdx += oursChange.baseCount;
1255
- oursIdx += oursChange.newCount;
1256
- theirsIdx += oursChange.baseCount;
1257
- } else if (!oursChange && theirsChange) {
1258
- applyChange(theirsChange, result);
1259
- baseIdx += theirsChange.baseCount;
1260
- oursIdx += theirsChange.baseCount;
1261
- theirsIdx += theirsChange.newCount;
1262
- } else if (oursChange && theirsChange) {
1263
- if (oursChange.baseCount === theirsChange.baseCount && oursChange.newLines.join("\n") === theirsChange.newLines.join("\n")) {
1264
- applyChange(oursChange, result);
1265
- baseIdx += oursChange.baseCount;
1266
- oursIdx += oursChange.newCount;
1267
- theirsIdx += theirsChange.newCount;
1268
- } else {
1269
- clean = false;
1270
- result.push("<<<<<<< ours");
1271
- for (const line of oursChange.newLines) result.push(line);
1272
- result.push("=======");
1273
- for (const line of theirsChange.newLines) result.push(line);
1274
- result.push(">>>>>>> theirs");
1275
- baseIdx += Math.max(oursChange.baseCount, theirsChange.baseCount);
1276
- oursIdx += oursChange.newCount;
1277
- theirsIdx += theirsChange.newCount;
1278
- }
1279
- }
1280
- }
1281
- return { clean, merged: result.join("\n") };
1282
- }
1283
- function applyChange(change, result) {
1284
- for (const line of change.newLines) {
1285
- result.push(line);
1286
- }
1287
- }
1288
- function computeLineChanges(baseLines, newLines) {
1289
- const changes = /* @__PURE__ */ new Map();
1290
- const matches = lcsMatch(baseLines, newLines);
1291
- let baseIdx = 0;
1292
- let newIdx = 0;
1293
- for (const match of matches) {
1294
- if (baseIdx < match.baseIdx || newIdx < match.newIdx) {
1295
- const baseCount = match.baseIdx - baseIdx;
1296
- const newCount = match.newIdx - newIdx;
1297
- if (baseCount > 0 || newCount > 0) {
1298
- changes.set(baseIdx, {
1299
- baseStart: baseIdx,
1300
- baseCount,
1301
- newCount,
1302
- newLines: newLines.slice(newIdx, newIdx + newCount)
1303
- });
1304
- }
1305
- }
1306
- baseIdx = match.baseIdx + 1;
1307
- newIdx = match.newIdx + 1;
1308
- }
1309
- if (baseIdx < baseLines.length || newIdx < newLines.length) {
1310
- const baseCount = baseLines.length - baseIdx;
1311
- const newCount = newLines.length - newIdx;
1312
- if (baseCount > 0 || newCount > 0) {
1313
- changes.set(baseIdx, {
1314
- baseStart: baseIdx,
1315
- baseCount,
1316
- newCount,
1317
- newLines: newLines.slice(newIdx)
1318
- });
1319
- }
1320
- }
1321
- return changes;
1322
- }
1323
- function lcsMatch(a, b) {
1324
- const n = a.length;
1325
- const m = b.length;
1326
- if (n === 0 || m === 0) return [];
1327
- const dp = Array.from(
1328
- { length: n + 1 },
1329
- () => new Array(m + 1).fill(0)
1330
- );
1331
- for (let i2 = 1; i2 <= n; i2++) {
1332
- for (let j2 = 1; j2 <= m; j2++) {
1333
- if (a[i2 - 1] === b[j2 - 1]) {
1334
- dp[i2][j2] = dp[i2 - 1][j2 - 1] + 1;
1335
- } else {
1336
- dp[i2][j2] = Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
1337
- }
1338
- }
1339
- }
1340
- const matches = [];
1341
- let i = n;
1342
- let j = m;
1343
- while (i > 0 && j > 0) {
1344
- if (a[i - 1] === b[j - 1]) {
1345
- matches.unshift({ baseIdx: i - 1, newIdx: j - 1 });
1346
- i--;
1347
- j--;
1348
- } else if (dp[i - 1][j] > dp[i][j - 1]) {
1349
- i--;
1350
- } else {
1351
- j--;
1352
- }
1353
- }
1354
- return matches;
1355
- }
1356
- var init_merge = __esm({
1357
- "src/vcs/merge.ts"() {
1358
- "use strict";
1359
- }
1360
- });
1361
-
1362
- // src/vcs/issue.ts
1363
- import { exec } from "child_process";
1364
- import { promisify } from "util";
1365
- import {
1366
- existsSync as existsSync3,
1367
- readFileSync as readFileSync3,
1368
- writeFileSync as writeFileSync3,
1369
- mkdirSync as mkdirSync2,
1370
- openSync,
1371
- closeSync,
1372
- unlinkSync
1373
- } from "fs";
1374
- import { join as join3, dirname as dirname2 } from "path";
1375
- function getIssueCounterPath(rootPath) {
1376
- return join3(rootPath, ".trellis", "issue-counter.json");
1377
- }
1378
- function nextIssueId(rootPath) {
1379
- const counterPath = getIssueCounterPath(rootPath);
1380
- const dir = dirname2(counterPath);
1381
- if (!existsSync3(dir)) mkdirSync2(dir, { recursive: true });
1382
- const lockPath = `${counterPath}.lock`;
1383
- const deadline = Date.now() + 5e3;
1384
- let lockFd;
1385
- while (Date.now() < deadline) {
1386
- try {
1387
- lockFd = openSync(lockPath, "wx");
1388
- break;
1389
- } catch (err) {
1390
- if (err?.code !== "EEXIST") {
1391
- throw err;
1392
- }
1393
- }
1394
- }
1395
- if (lockFd === void 0) {
1396
- throw new Error(
1397
- `Timed out waiting for issue counter lock: ${lockPath}. Another Trellis process may be stalled.`
1398
- );
1399
- }
1400
- try {
1401
- let counter = 0;
1402
- if (existsSync3(counterPath)) {
1403
- try {
1404
- counter = JSON.parse(readFileSync3(counterPath, "utf-8")).counter ?? 0;
1405
- } catch {
1406
- }
1407
- }
1408
- counter++;
1409
- writeFileSync3(counterPath, JSON.stringify({ counter }, null, 2));
1410
- return `TRL-${counter}`;
1411
- } finally {
1412
- closeSync(lockFd);
1413
- try {
1414
- unlinkSync(lockPath);
1415
- } catch {
1416
- }
1417
- }
1418
- }
1419
- function getIssueFact(ctx, entityId, attr) {
1420
- const facts = ctx.store.getFactsByEntity(entityId);
1421
- const matches = facts.filter((f) => f.a === attr);
1422
- return matches.length > 0 ? matches[matches.length - 1].v : void 0;
1423
- }
1424
- function getIssueLinks(ctx, entityId, attr) {
1425
- const links = ctx.store.getLinksByEntity(entityId);
1426
- return links.filter((l) => l.a === attr && l.e1 === entityId).map((l) => l.e2);
1427
- }
1428
- function getCriteriaForIssue(ctx, issueId) {
1429
- const eid = issueEntityId(issueId);
1430
- const criterionLinks = ctx.store.getLinksByAttribute("criterionOf").filter((l) => l.e2 === eid);
1431
- return criterionLinks.map((link) => {
1432
- const ceid = link.e1;
1433
- const facts = ctx.store.getFactsByEntity(ceid);
1434
- const getLast = (a) => {
1435
- const matches = facts.filter((f) => f.a === a);
1436
- return matches.length > 0 ? matches[matches.length - 1].v : void 0;
1437
- };
1438
- return {
1439
- id: ceid,
1440
- description: getLast("description"),
1441
- command: getLast("command"),
1442
- status: getLast("status"),
1443
- lastRunAt: getLast("lastRunAt"),
1444
- lastOutput: getLast("lastOutput")
1445
- };
1446
- });
1447
- }
1448
- function buildIssueInfo(ctx, entityId) {
1449
- const facts = ctx.store.getFactsByEntity(entityId);
1450
- const get = (a) => {
1451
- const matches = facts.filter((f) => f.a === a);
1452
- return matches.length > 0 ? matches[matches.length - 1].v : void 0;
1453
- };
1454
- const labelsStr = get("labels");
1455
- const labels = labelsStr ? labelsStr.split(",").filter(Boolean) : [];
1456
- const trackedOnLinks = getIssueLinks(ctx, entityId, "trackedOn");
1457
- const branchName = trackedOnLinks.length > 0 ? trackedOnLinks[0].replace(/^branch:/, "") : void 0;
1458
- const childOfLinks = getIssueLinks(ctx, entityId, "childOf");
1459
- const parentId = childOfLinks.length > 0 ? childOfLinks[0].replace(/^issue:/, "") : void 0;
1460
- const bareId = entityId.replace(/^issue:/, "");
1461
- const blockedByLinks = getIssueLinks(ctx, entityId, "blockedBy");
1462
- const blockedBy = blockedByLinks.map((e) => e.replace(/^issue:/, ""));
1463
- const allBlockedByLinks = ctx.store.getLinksByAttribute("blockedBy");
1464
- const blocking = allBlockedByLinks.filter((l) => l.e2 === entityId).map((l) => l.e1.replace(/^issue:/, ""));
1465
- const isBlocked = blockedByLinks.some((blockerEid) => {
1466
- const blockerStatus = getIssueFact(ctx, blockerEid, "status");
1467
- return blockerStatus !== "closed";
1468
- });
1469
- return {
1470
- id: bareId,
1471
- title: get("title"),
1472
- description: get("description"),
1473
- status: get("status"),
1474
- priority: get("priority"),
1475
- labels,
1476
- assignee: get("assignee"),
1477
- createdAt: get("createdAt"),
1478
- createdBy: get("createdBy"),
1479
- startedAt: get("startedAt"),
1480
- pausedAt: get("pausedAt"),
1481
- pauseNote: get("pauseNote") || void 0,
1482
- closedAt: get("closedAt"),
1483
- parentId,
1484
- branchName,
1485
- blockedBy,
1486
- blocking,
1487
- isBlocked,
1488
- criteria: getCriteriaForIssue(ctx, bareId)
1489
- };
1490
- }
1491
- async function createIssue(ctx, rootPath, title, opts) {
1492
- const id = nextIssueId(rootPath);
1493
- const op = await createVcsOp("vcs:issueCreate", {
1494
- agentId: ctx.agentId,
1495
- previousHash: ctx.getLastOp()?.hash,
1496
- vcs: {
1497
- issueId: id,
1498
- issueTitle: title,
1499
- issueDescription: opts?.description,
1500
- issueStatus: opts?.status ?? "backlog",
1501
- issuePriority: opts?.priority ?? "medium",
1502
- issueLabels: opts?.labels,
1503
- issueAssignee: opts?.assignee,
1504
- parentIssueId: opts?.parentId
1505
- }
1506
- });
1507
- await ctx.applyOp(op);
1508
- if (opts?.criteria) {
1509
- for (let i = 0; i < opts.criteria.length; i++) {
1510
- await addCriterion(
1511
- ctx,
1512
- id,
1513
- opts.criteria[i].description,
1514
- opts.criteria[i].command
1515
- );
1516
- }
1517
- }
1518
- return op;
1519
- }
1520
- async function updateIssue(ctx, id, updates) {
1521
- const issue = getIssue(ctx, id);
1522
- if (!issue) {
1523
- throw new Error(`Issue ${id} not found.`);
1524
- }
1525
- const vcs = { issueId: id };
1526
- if (updates.title !== void 0) vcs.issueTitle = updates.title;
1527
- if (updates.description !== void 0) vcs.issueDescription = updates.description;
1528
- if (updates.status !== void 0) {
1529
- vcs.oldIssueStatus = getIssueFact(ctx, issueEntityId(id), "status");
1530
- vcs.issueStatus = updates.status;
1531
- }
1532
- if (updates.priority !== void 0) vcs.issuePriority = updates.priority;
1533
- if (updates.labels !== void 0) vcs.issueLabels = updates.labels;
1534
- if (updates.assignee !== void 0) vcs.issueAssignee = updates.assignee;
1535
- if (updates.parentId !== void 0) {
1536
- const newParent = updates.parentId;
1537
- if (newParent === id) {
1538
- throw new Error(`Issue ${id} cannot be its own parent.`);
1539
- }
1540
- if (newParent) {
1541
- const parent = getIssue(ctx, newParent);
1542
- if (!parent) {
1543
- throw new Error(`Parent issue ${newParent} not found.`);
1544
- }
1545
- }
1546
- const currentParent = issue.parentId;
1547
- if (newParent !== currentParent) {
1548
- if (currentParent) vcs.oldParentIssueId = currentParent;
1549
- if (newParent) vcs.parentIssueId = newParent;
1550
- }
1551
- }
1552
- const op = await createVcsOp("vcs:issueUpdate", {
1553
- agentId: ctx.agentId,
1554
- previousHash: ctx.getLastOp()?.hash,
1555
- vcs
1556
- });
1557
- await ctx.applyOp(op);
1558
- return op;
1559
- }
1560
- async function startIssue(ctx, id, branchName) {
1561
- const eid = issueEntityId(id);
1562
- const status = getIssueFact(ctx, eid, "status");
1563
- if (status === "closed") {
1564
- throw new Error(`Cannot start closed issue ${id}. Reopen it first.`);
1565
- }
1566
- if (status === "in_progress") {
1567
- throw new Error(`Issue ${id} is already in progress.`);
1568
- }
1569
- const op = await createVcsOp("vcs:issueStart", {
1570
- agentId: ctx.agentId,
1571
- previousHash: ctx.getLastOp()?.hash,
1572
- vcs: {
1573
- issueId: id,
1574
- oldIssueStatus: status,
1575
- issueAssignee: ctx.agentId,
1576
- branchName
1577
- }
1578
- });
1579
- await ctx.applyOp(op);
1580
- return op;
1581
- }
1582
- async function pauseIssue(ctx, id, note) {
1583
- if (!note || !note.trim()) {
1584
- throw new Error(
1585
- `A pause note is required. Explain why the issue is paused and what must happen before resuming.`
1586
- );
1587
- }
1588
- const eid = issueEntityId(id);
1589
- const status = getIssueFact(ctx, eid, "status");
1590
- if (status !== "in_progress") {
1591
- throw new Error(
1592
- `Cannot pause issue ${id} \u2014 status is '${status}', expected 'in_progress'.`
1593
- );
1594
- }
1595
- const op = await createVcsOp("vcs:issuePause", {
1596
- agentId: ctx.agentId,
1597
- previousHash: ctx.getLastOp()?.hash,
1598
- vcs: {
1599
- issueId: id,
1600
- oldIssueStatus: status,
1601
- pauseNote: note.trim()
1602
- }
1603
- });
1604
- await ctx.applyOp(op);
1605
- return op;
1606
- }
1607
- async function resumeIssue(ctx, id) {
1608
- const eid = issueEntityId(id);
1609
- const status = getIssueFact(ctx, eid, "status");
1610
- if (status !== "paused") {
1611
- throw new Error(
1612
- `Cannot resume issue ${id} \u2014 status is '${status}', expected 'paused'.`
1613
- );
1614
- }
1615
- const op = await createVcsOp("vcs:issueResume", {
1616
- agentId: ctx.agentId,
1617
- previousHash: ctx.getLastOp()?.hash,
1618
- vcs: { issueId: id, oldIssueStatus: status }
1619
- });
1620
- await ctx.applyOp(op);
1621
- return op;
1622
- }
1623
- async function closeIssue(ctx, id, opts) {
1624
- const eid = issueEntityId(id);
1625
- const status = getIssueFact(ctx, eid, "status");
1626
- if (status === "closed") {
1627
- throw new Error(`Issue ${id} is already closed.`);
1628
- }
1629
- const criteria = getCriteriaForIssue(ctx, id);
1630
- const results = criteria.map((c) => ({
1631
- id: c.id,
1632
- description: c.description,
1633
- command: c.command,
1634
- status: c.status ?? "pending"
1635
- }));
1636
- const allPassed = results.length === 0 || results.every((r) => r.status === "passed");
1637
- if (!allPassed) {
1638
- const failing = results.filter((r) => r.status !== "passed");
1639
- throw new Error(
1640
- `Cannot close issue ${id}: ${failing.length} criteria not passing:
1641
- ` + failing.map((f) => ` - ${f.description ?? f.id} (${f.status})`).join("\n")
1642
- );
1643
- }
1644
- if (!opts?.confirm) {
1645
- return { criteriaResults: results };
1646
- }
1647
- const startedAt = getIssueFact(ctx, eid, "startedAt");
1648
- const op = await createVcsOp("vcs:issueClose", {
1649
- agentId: ctx.agentId,
1650
- previousHash: ctx.getLastOp()?.hash,
1651
- vcs: { issueId: id, oldIssueStatus: status }
1652
- });
1653
- await ctx.applyOp(op);
1654
- if (startedAt) {
1655
- const durationMs = Date.now() - new Date(startedAt).getTime();
1656
- ctx.store.addFacts([{ e: eid, a: "durationMs", v: durationMs }]);
1657
- }
1658
- return { op, criteriaResults: results };
1659
- }
1660
- async function triageIssue(ctx, id) {
1661
- const eid = issueEntityId(id);
1662
- const status = getIssueFact(ctx, eid, "status");
1663
- if (status !== "backlog") {
1664
- throw new Error(
1665
- `Cannot triage issue ${id} \u2014 status is '${status}', expected 'backlog'.`
1666
- );
1667
- }
1668
- const op = await createVcsOp("vcs:issueUpdate", {
1669
- agentId: ctx.agentId,
1670
- previousHash: ctx.getLastOp()?.hash,
1671
- vcs: {
1672
- issueId: id,
1673
- oldIssueStatus: status,
1674
- issueStatus: "queue"
1675
- }
1676
- });
1677
- await ctx.applyOp(op);
1678
- return op;
1679
- }
1680
- async function reopenIssue(ctx, id) {
1681
- const eid = issueEntityId(id);
1682
- const status = getIssueFact(ctx, eid, "status");
1683
- if (status !== "closed") {
1684
- throw new Error(
1685
- `Cannot reopen issue ${id} \u2014 status is '${status}', expected 'closed'.`
1686
- );
1687
- }
1688
- const op = await createVcsOp("vcs:issueReopen", {
1689
- agentId: ctx.agentId,
1690
- previousHash: ctx.getLastOp()?.hash,
1691
- vcs: { issueId: id, oldIssueStatus: status }
1692
- });
1693
- await ctx.applyOp(op);
1694
- return op;
1695
- }
1696
- async function assignIssue(ctx, id, agentId) {
1697
- return updateIssue(ctx, id, { assignee: agentId });
1698
- }
1699
- async function blockIssue(ctx, id, blockedById) {
1700
- const eid = issueEntityId(id);
1701
- const blockerEid = issueEntityId(blockedById);
1702
- if (!getIssueFact(ctx, eid, "type")) {
1703
- throw new Error(`Issue ${id} not found.`);
1704
- }
1705
- if (!getIssueFact(ctx, blockerEid, "type")) {
1706
- throw new Error(`Blocking issue ${blockedById} not found.`);
1707
- }
1708
- if (id === blockedById) {
1709
- throw new Error(`Issue cannot block itself.`);
1710
- }
1711
- const op = await createVcsOp("vcs:issueBlock", {
1712
- agentId: ctx.agentId,
1713
- previousHash: ctx.getLastOp()?.hash,
1714
- vcs: { issueId: id, blockedByIssueId: blockedById }
1715
- });
1716
- await ctx.applyOp(op);
1717
- return op;
1718
- }
1719
- async function unblockIssue(ctx, id, blockedById) {
1720
- const op = await createVcsOp("vcs:issueUnblock", {
1721
- agentId: ctx.agentId,
1722
- previousHash: ctx.getLastOp()?.hash,
1723
- vcs: { issueId: id, blockedByIssueId: blockedById }
1724
- });
1725
- await ctx.applyOp(op);
1726
- return op;
1727
- }
1728
- async function addCriterion(ctx, issueId, description, command) {
1729
- const existing = getCriteriaForIssue(ctx, issueId);
1730
- const index = existing.length + 1;
1731
- const cid = criterionEntityId(issueId, index);
1732
- const op = await createVcsOp("vcs:criterionAdd", {
1733
- agentId: ctx.agentId,
1734
- previousHash: ctx.getLastOp()?.hash,
1735
- vcs: {
1736
- issueId,
1737
- criterionId: cid,
1738
- criterionDescription: description,
1739
- criterionCommand: command
1740
- }
1741
- });
1742
- await ctx.applyOp(op);
1743
- return op;
1744
- }
1745
- async function setCriterionStatus(ctx, issueId, criterionIndex, status) {
1746
- const criteria = getCriteriaForIssue(ctx, issueId);
1747
- if (criterionIndex < 1 || criterionIndex > criteria.length) {
1748
- throw new Error(
1749
- `Criterion index ${criterionIndex} out of range (1\u2013${criteria.length})`
1750
- );
1751
- }
1752
- const c = criteria[criterionIndex - 1];
1753
- const op = await createVcsOp("vcs:criterionUpdate", {
1754
- agentId: ctx.agentId,
1755
- previousHash: ctx.getLastOp()?.hash,
1756
- vcs: {
1757
- issueId,
1758
- criterionId: c.id,
1759
- criterionStatus: status
1760
- }
1761
- });
1762
- await ctx.applyOp(op);
1763
- return op;
1764
- }
1765
- async function runCriteria(ctx, issueId, rootPath) {
1766
- const criteria = getCriteriaForIssue(ctx, issueId);
1767
- const results = [];
1768
- for (const c of criteria) {
1769
- if (!c.command) {
1770
- results.push({
1771
- id: c.id,
1772
- description: c.description,
1773
- status: c.status ?? "skipped"
1774
- });
1775
- continue;
1776
- }
1777
- let status = "failed";
1778
- let output = "";
1779
- let exitCode = 1;
1780
- try {
1781
- const result = await execAsync(c.command, {
1782
- cwd: rootPath,
1783
- timeout: 12e4
1784
- });
1785
- output = (result.stdout + "\n" + result.stderr).trim();
1786
- exitCode = 0;
1787
- status = "passed";
1788
- } catch (err) {
1789
- output = (err.stdout ?? "") + "\n" + (err.stderr ?? err.message ?? "");
1790
- output = output.trim();
1791
- exitCode = err.code ?? 1;
1792
- status = "failed";
1793
- }
1794
- const updateOp = await createVcsOp("vcs:criterionUpdate", {
1795
- agentId: ctx.agentId,
1796
- previousHash: ctx.getLastOp()?.hash,
1797
- vcs: {
1798
- criterionId: c.id,
1799
- criterionStatus: status,
1800
- criterionOutput: output.slice(0, 4096)
1801
- }
1802
- });
1803
- await ctx.applyOp(updateOp);
1804
- results.push({
1805
- id: c.id,
1806
- description: c.description,
1807
- command: c.command,
1808
- status,
1809
- output,
1810
- exitCode
1811
- });
1812
- }
1813
- return results;
1814
- }
1815
- function listIssues(ctx, filters) {
1816
- const issueFacts = ctx.store.getFactsByAttribute("type").filter((f) => f.v === "Issue");
1817
- let issues = issueFacts.map((f) => buildIssueInfo(ctx, f.e));
1818
- if (filters?.status) {
1819
- issues = issues.filter((i) => i.status === filters.status);
1820
- }
1821
- if (filters?.assignee) {
1822
- issues = issues.filter((i) => i.assignee === filters.assignee);
1823
- }
1824
- if (filters?.label) {
1825
- issues = issues.filter((i) => i.labels.includes(filters.label));
1826
- }
1827
- if (filters?.parentId) {
1828
- issues = issues.filter((i) => i.parentId === filters.parentId);
1829
- }
1830
- if (filters?.blocked !== void 0) {
1831
- issues = issues.filter((i) => i.isBlocked === filters.blocked);
1832
- }
1833
- return issues;
1834
- }
1835
- function getIssue(ctx, id) {
1836
- const eid = issueEntityId(id);
1837
- const typeFact = ctx.store.getFactsByEntity(eid).find((f) => f.a === "type" && f.v === "Issue");
1838
- if (!typeFact) return null;
1839
- return buildIssueInfo(ctx, eid);
1840
- }
1841
- function getActiveIssues(ctx) {
1842
- return listIssues(ctx, { status: "in_progress" });
1843
- }
1844
- function checkCompletionReadiness(ctx) {
1845
- const all = listIssues(ctx);
1846
- const queue = all.filter((i) => i.status === "queue");
1847
- const paused = all.filter((i) => i.status === "paused");
1848
- const inProgress = all.filter((i) => i.status === "in_progress");
1849
- const ready = queue.length === 0 && paused.length === 0 && inProgress.length === 0;
1850
- const parts = [];
1851
- if (ready) {
1852
- parts.push("\u2713 All clear \u2014 no queue, paused, or in-progress issues.");
1853
- } else {
1854
- parts.push("\u2717 Not ready for completion:");
1855
- if (queue.length > 0) {
1856
- parts.push(
1857
- ` Queue (${queue.length}): ${queue.map((i) => i.id).join(", ")}`
1858
- );
1859
- }
1860
- if (inProgress.length > 0) {
1861
- parts.push(
1862
- ` In progress (${inProgress.length}): ${inProgress.map((i) => i.id).join(", ")}`
1863
- );
1864
- }
1865
- if (paused.length > 0) {
1866
- parts.push(
1867
- ` Paused (${paused.length}): ${paused.map((i) => `${i.id}${i.pauseNote ? ` \u2014 ${i.pauseNote}` : ""}`).join(", ")}`
1868
- );
1869
- }
1870
- }
1871
- return { ready, queue, paused, inProgress, summary: parts.join("\n") };
1872
- }
1873
- var execAsync;
1874
- var init_issue = __esm({
1875
- "src/vcs/issue.ts"() {
1876
- "use strict";
1877
- init_ops();
1878
- init_types();
1879
- execAsync = promisify(exec);
1880
- }
1881
- });
1882
-
1883
- // src/vcs/op-log.ts
1884
- import {
1885
- existsSync as existsSync4,
1886
- mkdirSync as mkdirSync3,
1887
- readFileSync as readFileSync4,
1888
- writeFileSync as writeFileSync4,
1889
- copyFileSync,
1890
- openSync as openSync2,
1891
- closeSync as closeSync2,
1892
- unlinkSync as unlinkSync2,
1893
- renameSync
1894
- } from "fs";
1895
- import { dirname as dirname3 } from "path";
1896
- function lockTimeoutMs() {
1897
- const raw = process.env.TRELLIS_OPLOG_LOCK_MS;
1898
- if (!raw) return 5e3;
1899
- const parsed = Number(raw);
1900
- return Number.isFinite(parsed) && parsed > 0 ? parsed : 5e3;
1901
- }
1902
- var JsonOpLog, LaneOpLog;
1903
- var init_op_log = __esm({
1904
- "src/vcs/op-log.ts"() {
1905
- "use strict";
1906
- JsonOpLog = class {
1907
- ops = [];
1908
- filePath;
1909
- lockPath;
1910
- constructor(filePath) {
1911
- this.filePath = filePath;
1912
- this.lockPath = `${filePath}.lock`;
1913
- }
1914
- get path() {
1915
- return this.filePath;
1916
- }
1917
- load() {
1918
- if (existsSync4(this.filePath)) {
1919
- const raw = readFileSync4(this.filePath, "utf-8");
1920
- try {
1921
- this.ops = JSON.parse(raw);
1922
- } catch {
1923
- const backupPath = this.filePath + ".bak";
1924
- if (existsSync4(backupPath)) {
1925
- const backupRaw = readFileSync4(backupPath, "utf-8");
1926
- this.ops = JSON.parse(backupRaw);
1927
- writeFileSync4(this.filePath, backupRaw);
1928
- } else {
1929
- throw new Error(
1930
- `Corrupted ops log at ${this.filePath} and no backup found. Run \`trellis repair\` to attempt recovery.`
1931
- );
1932
- }
1933
- }
1934
- }
1935
- }
1936
- append(op) {
1937
- this.withLock(() => {
1938
- const diskOps = this.readOpsFromDisk();
1939
- this.ops = diskOps;
1940
- if (this.ops.some((existing) => existing.hash === op.hash)) {
1941
- return;
1942
- }
1943
- this.ops.push(op);
1944
- this.flush();
1945
- });
1946
- }
1947
- readAll() {
1948
- return [...this.ops];
1949
- }
1950
- getLastOp() {
1951
- return this.ops.length > 0 ? this.ops[this.ops.length - 1] : void 0;
1952
- }
1953
- count() {
1954
- return this.ops.length;
1955
- }
1956
- flush() {
1957
- const dir = dirname3(this.filePath);
1958
- if (!existsSync4(dir)) mkdirSync3(dir, { recursive: true });
1959
- if (existsSync4(this.filePath)) {
1960
- const backupPath = this.filePath + ".bak";
1961
- try {
1962
- copyFileSync(this.filePath, backupPath);
1963
- } catch {
1964
- }
1965
- }
1966
- const tempPath = `${this.filePath}.tmp`;
1967
- writeFileSync4(tempPath, JSON.stringify(this.ops, null, 2));
1968
- renameSync(tempPath, this.filePath);
1969
- }
1970
- readOpsFromDisk() {
1971
- if (!existsSync4(this.filePath)) {
1972
- return [];
1973
- }
1974
- const raw = readFileSync4(this.filePath, "utf-8");
1975
- try {
1976
- const parsed = JSON.parse(raw);
1977
- return Array.isArray(parsed) ? parsed : [];
1978
- } catch {
1979
- const backupPath = this.filePath + ".bak";
1980
- if (existsSync4(backupPath)) {
1981
- const backupRaw = readFileSync4(backupPath, "utf-8");
1982
- const parsedBackup = JSON.parse(backupRaw);
1983
- writeFileSync4(this.filePath, backupRaw);
1984
- return Array.isArray(parsedBackup) ? parsedBackup : [];
1985
- }
1986
- throw new Error(
1987
- `Corrupted ops log at ${this.filePath} and no backup found. Run \`trellis repair\` to attempt recovery.`
1988
- );
1989
- }
1990
- }
1991
- withLock(fn) {
1992
- const dir = dirname3(this.filePath);
1993
- if (!existsSync4(dir)) mkdirSync3(dir, { recursive: true });
1994
- const deadline = Date.now() + lockTimeoutMs();
1995
- let lockFd;
1996
- while (Date.now() < deadline) {
1997
- try {
1998
- lockFd = openSync2(this.lockPath, "wx");
1999
- break;
2000
- } catch (err) {
2001
- if (err?.code !== "EEXIST") {
2002
- throw err;
2003
- }
2004
- }
2005
- }
2006
- if (lockFd === void 0) {
2007
- throw new Error(
2008
- `Timed out waiting for ops log lock: ${this.lockPath}. Another Trellis process may be stalled.`
2009
- );
2010
- }
2011
- try {
2012
- fn();
2013
- } finally {
2014
- closeSync2(lockFd);
2015
- try {
2016
- unlinkSync2(this.lockPath);
2017
- } catch {
2018
- }
2019
- }
2020
- }
2021
- static repair(filePath) {
2022
- if (!existsSync4(filePath)) {
2023
- return { recovered: 0, lost: 0 };
2024
- }
2025
- const raw = readFileSync4(filePath, "utf-8");
2026
- try {
2027
- const ops = JSON.parse(raw);
2028
- return { recovered: ops.length, lost: 0 };
2029
- } catch {
2030
- }
2031
- const lastHash = raw.lastIndexOf('"hash": "trellis:op:');
2032
- if (lastHash === -1) {
2033
- const bakPath = filePath + ".bak";
2034
- if (existsSync4(bakPath)) {
2035
- const bakRaw = readFileSync4(bakPath, "utf-8");
2036
- try {
2037
- const ops = JSON.parse(bakRaw);
2038
- writeFileSync4(filePath, bakRaw);
2039
- return { recovered: ops.length, lost: 0 };
2040
- } catch {
2041
- }
2042
- }
2043
- writeFileSync4(filePath, "[]");
2044
- return { recovered: 0, lost: -1 };
2045
- }
2046
- const endOfLine = raw.indexOf("\n", lastHash);
2047
- const closingBrace = raw.indexOf(" }", endOfLine);
2048
- if (closingBrace === -1) {
2049
- writeFileSync4(filePath, "[]");
2050
- return { recovered: 0, lost: -1 };
2051
- }
2052
- const fixed = raw.slice(0, closingBrace + 3) + "\n]";
2053
- try {
2054
- const ops = JSON.parse(fixed);
2055
- writeFileSync4(filePath + ".corrupted", raw);
2056
- writeFileSync4(filePath, fixed);
2057
- return { recovered: ops.length, lost: 0 };
2058
- } catch {
2059
- writeFileSync4(filePath + ".corrupted", raw);
2060
- writeFileSync4(filePath, "[]");
2061
- return { recovered: 0, lost: -1 };
2062
- }
2063
- }
2064
- };
2065
- LaneOpLog = class extends JsonOpLog {
2066
- constructor(laneDir2) {
2067
- super(`${laneDir2}/ops.json`);
2068
- }
2069
- };
2070
- }
2071
- });
2072
-
2073
- // src/vcs/lane.ts
2074
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync5, readdirSync } from "fs";
2075
- import { join as join4 } from "path";
2076
- import { randomUUID } from "crypto";
2077
- function lanesRoot(trellisDir) {
2078
- return join4(trellisDir, "lanes");
2079
- }
2080
- function laneDir(trellisDir, laneId) {
2081
- return join4(lanesRoot(trellisDir), laneId);
2082
- }
2083
- function laneMetaPath(trellisDir, laneId) {
2084
- return join4(laneDir(trellisDir, laneId), "meta.json");
2085
- }
2086
- function newLaneId() {
2087
- return `lane-${randomUUID()}`;
2088
- }
2089
- function loadLaneMeta(trellisDir, laneId) {
2090
- const path = laneMetaPath(trellisDir, laneId);
2091
- if (!existsSync5(path)) return void 0;
2092
- return JSON.parse(readFileSync5(path, "utf-8"));
2093
- }
2094
- function saveLaneMeta(trellisDir, meta) {
2095
- const dir = laneDir(trellisDir, meta.id);
2096
- if (!existsSync5(dir)) mkdirSync4(dir, { recursive: true });
2097
- writeFileSync5(
2098
- laneMetaPath(trellisDir, meta.id),
2099
- JSON.stringify(meta, null, 2) + "\n"
2100
- );
2101
- }
2102
- function listLaneIds(trellisDir) {
2103
- const root = lanesRoot(trellisDir);
2104
- if (!existsSync5(root)) return [];
2105
- return readdirSync(root, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.startsWith("lane-")).map((d) => d.name);
2106
- }
2107
- function resolveLaneHeadFromJournal(meta, laneOps) {
2108
- return laneOps.at(-1)?.hash ?? meta.headOpHash ?? meta.baseOpHash;
2109
- }
2110
- function createLaneMeta(trellisDir, params) {
2111
- const now = (/* @__PURE__ */ new Date()).toISOString();
2112
- const meta = {
2113
- id: newLaneId(),
2114
- status: "active",
2115
- baseBranch: params.baseBranch,
2116
- baseOpHash: params.baseOpHash,
2117
- targetBranch: params.targetBranch ?? params.baseBranch,
2118
- headOpHash: params.baseOpHash,
2119
- agentId: params.agentId,
2120
- issueId: params.issueId,
2121
- sessionId: params.sessionId,
2122
- parentLaneId: params.parentLaneId,
2123
- forkKind: params.forkKind,
2124
- forkedAt: params.forkedAt,
2125
- virtualBaseOpHash: params.virtualBaseOpHash,
2126
- worktreePath: params.worktreePath,
2127
- leaseExpiresAt: params.leaseExpiresAt,
2128
- createdAt: now,
2129
- updatedAt: now
2130
- };
2131
- saveLaneMeta(trellisDir, meta);
2132
- return meta;
2133
- }
2134
- function updateLaneHead(trellisDir, laneId, headOpHash) {
2135
- const meta = loadLaneMeta(trellisDir, laneId);
2136
- if (!meta) return;
2137
- meta.headOpHash = headOpHash;
2138
- meta.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
2139
- saveLaneMeta(trellisDir, meta);
2140
- }
2141
- function listLaneMetas(trellisDir) {
2142
- return listLaneIds(trellisDir).map((id) => loadLaneMeta(trellisDir, id)).filter((m) => m !== void 0);
2143
- }
2144
- var init_lane = __esm({
2145
- "src/vcs/lane.ts"() {
2146
- "use strict";
2147
- }
2148
- });
2149
-
2150
- export {
2151
- decompose,
2152
- init_decompose,
2153
- BlobStore,
2154
- init_blob_store,
2155
- shouldAdvanceBranchHead,
2156
- getBranchHeadOpHash,
2157
- createBranch,
2158
- switchBranch,
2159
- listBranches,
2160
- deleteBranch,
2161
- saveBranchState,
2162
- loadBranchState,
2163
- init_branch,
2164
- createMilestone,
2165
- listMilestones,
2166
- init_milestone,
2167
- createCheckpoint,
2168
- listCheckpoints,
2169
- init_checkpoint,
2170
- diffFileStates,
2171
- buildFileStateAtOp,
2172
- diffOpRange,
2173
- generateUnifiedDiff,
2174
- myersDiff,
2175
- init_diff,
2176
- threeWayMerge,
2177
- threeWayTextMerge,
2178
- init_merge,
2179
- createIssue,
2180
- updateIssue,
2181
- startIssue,
2182
- pauseIssue,
2183
- resumeIssue,
2184
- closeIssue,
2185
- triageIssue,
2186
- reopenIssue,
2187
- assignIssue,
2188
- blockIssue,
2189
- unblockIssue,
2190
- addCriterion,
2191
- setCriterionStatus,
2192
- runCriteria,
2193
- listIssues,
2194
- getIssue,
2195
- getActiveIssues,
2196
- checkCompletionReadiness,
2197
- init_issue,
2198
- JsonOpLog,
2199
- LaneOpLog,
2200
- init_op_log,
2201
- lanesRoot,
2202
- laneDir,
2203
- laneMetaPath,
2204
- newLaneId,
2205
- loadLaneMeta,
2206
- saveLaneMeta,
2207
- listLaneIds,
2208
- resolveLaneHeadFromJournal,
2209
- createLaneMeta,
2210
- updateLaneHead,
2211
- listLaneMetas,
2212
- init_lane
2213
- };