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.
- package/CHANGELOG.md +7 -0
- package/README.md +61 -171
- package/dist/browser/index.d.ts +18 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +95 -0
- package/dist/{chunk-3ETIIPPB.js → chunk-2W3MHTMG.js} +190 -27
- package/dist/{chunk-MBNBTJCR.js → chunk-6GEZ6DN5.js} +2 -2
- package/dist/chunk-7ZFRVCUE.js +211 -0
- package/dist/chunk-CAG4FULI.js +121 -0
- package/dist/{chunk-GD5QJXSS.js → chunk-FWRS4CSC.js} +2 -2
- package/dist/{chunk-36VSSTL7.js → chunk-GU374KWZ.js} +1 -1
- package/dist/{chunk-3GDRZTAW.js → chunk-H6JB3PZ3.js} +1 -1
- package/dist/{chunk-ZHVI7BQN.js → chunk-IZX2PLIB.js} +191 -28
- package/dist/chunk-J2TXUFCZ.js +289 -0
- package/dist/{chunk-BJ7EZ7FP.js → chunk-JHHEXEDM.js} +17 -0
- package/dist/{chunk-X762XWVC.js → chunk-KIJITUZB.js} +1 -1
- package/dist/{chunk-7WQGTFVJ.js → chunk-L5N5PR2S.js} +130 -75
- package/dist/{chunk-BXSTJVE3.js → chunk-N6MYZT4O.js} +2 -1
- package/dist/{chunk-KKDRMIV6.js → chunk-QB5ISE47.js} +1 -1
- package/dist/{chunk-HO4TURXW.js → chunk-S3XLFZ27.js} +28 -11
- package/dist/{chunk-BQBUVMRN.js → chunk-S6GBJ2ZB.js} +25 -14
- package/dist/{chunk-IUWVEEOX.js → chunk-SGMDTWJJ.js} +4 -0
- package/dist/{chunk-FZVCXDYW.js → chunk-UQISRW6T.js} +65 -12
- package/dist/{chunk-B3ZIFA2O.js → chunk-VBYUTRXX.js} +1 -1
- package/dist/cli/deploy-cli.d.ts +18 -0
- package/dist/cli/deploy-cli.d.ts.map +1 -0
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +153 -100
- package/dist/client/index.browser.d.ts +29 -0
- package/dist/client/index.browser.d.ts.map +1 -0
- package/dist/client/index.browser.js +64 -0
- package/dist/client/index.js +10 -10
- package/dist/client/sdk.browser.d.ts +104 -0
- package/dist/client/sdk.browser.d.ts.map +1 -0
- package/dist/{sdk-LZ7W3EEY.js → client/sdk.browser.js} +2 -2
- package/dist/client/sdk.js +1 -1
- package/dist/core/index.js +6 -6
- package/dist/db/index.js +18 -18
- package/dist/db/inspector.js +6 -6
- package/dist/db/trellis.css +1 -1
- package/dist/decisions/index.js +2 -2
- package/dist/deploy-LQ7GUGJ5.js +9 -0
- package/dist/{deploy-66EC752W.js → deploy-YNVG5E4E.js} +1 -1
- package/dist/deploy-cli-24BCHAXY.js +67 -0
- package/dist/deploy-cli-C35HTITO.js +67 -0
- package/dist/embeddings/auto-embed.d.ts.map +1 -1
- package/dist/embeddings/index.js +1 -1
- package/dist/embeddings/store.d.ts.map +1 -1
- package/dist/engine.d.ts +27 -7
- package/dist/engine.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/react/index.js +2 -2
- package/dist/react/schema-hooks.js +7 -7
- package/dist/{remote-manager-SG545WNY.js → remote-manager-ODE3T2KR.js} +5 -5
- package/dist/scaffold/seed.d.ts.map +1 -1
- package/dist/scaffold/write.d.ts +1 -1
- package/dist/scaffold/write.d.ts.map +1 -1
- package/dist/schema/index.js +4 -3
- package/dist/server/deploy-meta.d.ts +25 -0
- package/dist/server/deploy-meta.d.ts.map +1 -0
- package/dist/server/deploy.d.ts +7 -2
- package/dist/server/deploy.d.ts.map +1 -1
- package/dist/server/index.d.ts +5 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +52 -18
- package/dist/server/realtime.d.ts +4 -1
- package/dist/server/realtime.d.ts.map +1 -1
- package/dist/server/server.d.ts +1 -0
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/sprites.d.ts +18 -1
- package/dist/server/sprites.d.ts.map +1 -1
- package/dist/server/tenancy.d.ts +4 -0
- package/dist/server/tenancy.d.ts.map +1 -1
- package/dist/server/usage-meter.d.ts +53 -0
- package/dist/server/usage-meter.d.ts.map +1 -0
- package/dist/{server-IGBIIJSF.js → server-MB3OPSPI.js} +6 -1
- package/dist/{server-BOH7M4TT.js → server-ZVWGLLRF.js} +7 -2
- package/dist/sprites-KQGTGM6W.js +27 -0
- package/dist/{tenancy-S676GLID.js → tenancy-NBLLGCNJ.js} +1 -1
- package/dist/{tenancy-OP34NFFS.js → tenancy-POMWQP6M.js} +5 -3
- package/dist/vcs/index.js +3 -3
- package/dist/vcs/types.d.ts +2 -0
- package/dist/vcs/types.d.ts.map +1 -1
- package/package.json +17 -6
- package/dist/better-sqlite-backend-PII64VE6.js +0 -159
- package/dist/better-sqlite-backend-VUBVQ7SN.js +0 -209
- package/dist/better-sqlite-backend-Y3AQN65E.js +0 -159
- package/dist/better-sqlite-backend-Y3M7DXOX.js +0 -159
- package/dist/boot-middleware-FJDGVUM4.js +0 -9
- package/dist/boot-middleware-MLJUTLUM.js +0 -9
- package/dist/boot-middleware-ZZPDZNYR.js +0 -9
- package/dist/chunk-276LKLZR.js +0 -359
- package/dist/chunk-2CX5UCRV.js +0 -111
- package/dist/chunk-2ESYSVXG.js +0 -48
- package/dist/chunk-2I5JU73Y.js +0 -340
- package/dist/chunk-2UNSRRNB.js +0 -171
- package/dist/chunk-335UEHQR.js +0 -907
- package/dist/chunk-4CF2L46N.js +0 -29
- package/dist/chunk-5CINODKT.js +0 -41
- package/dist/chunk-5ESGWJ5L.js +0 -45
- package/dist/chunk-5FGLQGMF.js +0 -1676
- package/dist/chunk-5IQZFITY.js +0 -357
- package/dist/chunk-5JWFAQDW.js +0 -150
- package/dist/chunk-62UHTVU3.js +0 -141
- package/dist/chunk-66UJQT2I.js +0 -35
- package/dist/chunk-6RNNVOAS.js +0 -355
- package/dist/chunk-6X23PZ6H.js +0 -969
- package/dist/chunk-7C7LL6MB.js +0 -315
- package/dist/chunk-7F4HM3JG.js +0 -394
- package/dist/chunk-7KJ7T4D3.js +0 -910
- package/dist/chunk-7QQQNUB5.js +0 -92
- package/dist/chunk-7QWJBS72.js +0 -946
- package/dist/chunk-7R36OZK3.js +0 -957
- package/dist/chunk-7UPKCOHE.js +0 -946
- package/dist/chunk-A3BONW34.js +0 -35
- package/dist/chunk-AHMDVHBL.js +0 -141
- package/dist/chunk-AW6A4OZY.js +0 -171
- package/dist/chunk-BJ5EKCJ5.js +0 -458
- package/dist/chunk-BRGYDO7D.js +0 -223
- package/dist/chunk-C5KPSEDN.js +0 -359
- package/dist/chunk-CCTNYTUC.js +0 -940
- package/dist/chunk-CYQ2OVD2.js +0 -35
- package/dist/chunk-DDAK7YOE.js +0 -299
- package/dist/chunk-DRILKBY6.js +0 -965
- package/dist/chunk-DXE24HNN.js +0 -45
- package/dist/chunk-E7ICUNW7.js +0 -945
- package/dist/chunk-EAL2PUOE.js +0 -10022
- package/dist/chunk-ELT7MLZB.js +0 -109
- package/dist/chunk-EWEIRF6G.js +0 -171
- package/dist/chunk-F5VDGQJC.js +0 -120
- package/dist/chunk-FCPL3IEQ.js +0 -98
- package/dist/chunk-FDPKYSV7.js +0 -96
- package/dist/chunk-FES3TC24.js +0 -171
- package/dist/chunk-FOWKR5TW.js +0 -129
- package/dist/chunk-G3XIHPSQ.js +0 -361
- package/dist/chunk-GKJLYLVU.js +0 -359
- package/dist/chunk-GNXHXNVQ.js +0 -113
- package/dist/chunk-GSR6ILTM.js +0 -277
- package/dist/chunk-GWRIMRSX.js +0 -45
- package/dist/chunk-H72UW2HJ.js +0 -45
- package/dist/chunk-HGI74PXB.js +0 -7417
- package/dist/chunk-HGRA37LO.js +0 -145
- package/dist/chunk-HXSRQO3L.js +0 -145
- package/dist/chunk-IG2X2BVO.js +0 -373
- package/dist/chunk-IIJVDD54.js +0 -373
- package/dist/chunk-IX4BBOIT.js +0 -394
- package/dist/chunk-IXU4RMQM.js +0 -349
- package/dist/chunk-J6OUIION.js +0 -857
- package/dist/chunk-K46RNFDE.js +0 -171
- package/dist/chunk-KAGU2XMY.js +0 -359
- package/dist/chunk-KCVYJNVX.js +0 -394
- package/dist/chunk-KYLIQ3YI.js +0 -399
- package/dist/chunk-KZYJJIRN.js +0 -946
- package/dist/chunk-LFOX744K.js +0 -96
- package/dist/chunk-LHOVJEWR.js +0 -7403
- package/dist/chunk-LOPXTPF3.js +0 -120
- package/dist/chunk-LZR3VIFU.js +0 -907
- package/dist/chunk-MCKGQKYU.js +0 -15
- package/dist/chunk-MU6EOS6F.js +0 -0
- package/dist/chunk-MWRQ2TXT.js +0 -357
- package/dist/chunk-NO73OK2I.js +0 -102
- package/dist/chunk-NPE2YJHK.js +0 -394
- package/dist/chunk-NSDXU6QX.js +0 -2213
- package/dist/chunk-NXIRDSZ6.js +0 -145
- package/dist/chunk-NZWFNHCS.js +0 -407
- package/dist/chunk-OB4CMMX6.js +0 -260
- package/dist/chunk-OBKU74FU.js +0 -7411
- package/dist/chunk-OQNDWRDE.js +0 -145
- package/dist/chunk-OVWAJMB6.js +0 -394
- package/dist/chunk-PDHLBTTD.js +0 -45
- package/dist/chunk-PEQNRKLG.js +0 -210
- package/dist/chunk-QERIS7QY.js +0 -7403
- package/dist/chunk-QHA2DM3C.js +0 -373
- package/dist/chunk-QLM4HSYC.js +0 -45
- package/dist/chunk-S2OT3TQJ.js +0 -283
- package/dist/chunk-S5PHSN5K.js +0 -907
- package/dist/chunk-SIAA4J6H.js +0 -21
- package/dist/chunk-SQBNLUIA.js +0 -171
- package/dist/chunk-STLN6A6B.js +0 -355
- package/dist/chunk-U7HV7GU2.js +0 -17
- package/dist/chunk-UGZHOGRA.js +0 -39
- package/dist/chunk-UICF66FS.js +0 -359
- package/dist/chunk-UK3EQREE.js +0 -1078
- package/dist/chunk-UPKPURVY.js +0 -394
- package/dist/chunk-URC74VCN.js +0 -959
- package/dist/chunk-V3G7Q3QQ.js +0 -102
- package/dist/chunk-VUJ6SA55.js +0 -303
- package/dist/chunk-VUJQLIML.js +0 -141
- package/dist/chunk-VWKLKLYB.js +0 -267
- package/dist/chunk-VYC3RZMK.js +0 -277
- package/dist/chunk-WMERMSWI.js +0 -154
- package/dist/chunk-WMR777PO.js +0 -2213
- package/dist/chunk-X46IECAC.js +0 -517
- package/dist/chunk-XEN3OEYY.js +0 -2234
- package/dist/chunk-XZQ66K7A.js +0 -124
- package/dist/chunk-YY3GF7DH.js +0 -7411
- package/dist/chunk-ZE5R3H4C.js +0 -907
- package/dist/chunk-ZMN5LUB7.js +0 -7411
- package/dist/chunk-ZUV4MD5U.js +0 -94
- package/dist/config-KMY6RRK3.js +0 -19
- package/dist/config-VDMK2DUJ.js +0 -19
- package/dist/deploy-SO7AFK6L.js +0 -9
- package/dist/engine-7GXBM4IT.js +0 -7
- package/dist/engine-NSR6BIF7.js +0 -7
- package/dist/factory-2P64OC7M.js +0 -34
- package/dist/factory-YP4ZZK5K.js +0 -34
- package/dist/import-AJLYHFIA.js +0 -10
- package/dist/launch-explorer-AI6L2AWJ.js +0 -91
- package/dist/query-GJL3CVGN.js +0 -31
- package/dist/query-MBJSK3EB.js +0 -31
- package/dist/query-TJHEVSKL.js +0 -31
- package/dist/query-WUIKOHPE.js +0 -524
- package/dist/remote-manager-BLJZ6KF6.js +0 -224
- package/dist/remote-manager-BPYE46SI.js +0 -223
- package/dist/remote-manager-FMESEAFA.js +0 -224
- package/dist/remote-manager-K2WZ5RHY.js +0 -223
- package/dist/remote-manager-SG5PQKCH.js +0 -224
- package/dist/remote-manager-WGP6MIQZ.js +0 -224
- package/dist/sdk-3KBB3E6V.js +0 -16
- package/dist/sdk-4ZQO6UTU.js +0 -16
- package/dist/sdk-6JA6GV3G.js +0 -16
- package/dist/sdk-6VTP5UTZ.js +0 -16
- package/dist/sdk-7VS6TYCD.js +0 -16
- package/dist/sdk-HQ3FTLRT.js +0 -16
- package/dist/sdk-HZ7NU3XE.js +0 -15
- package/dist/sdk-KBQE2OXT.js +0 -16
- package/dist/sdk-MSCDZ7JU.js +0 -15
- package/dist/sdk-WNZOGO3S.js +0 -15
- package/dist/sdk-YLV33ZCW.js +0 -9
- package/dist/sdk-ZSUD2LLJ.js +0 -16
- package/dist/server-AFXFFBWA.js +0 -11
- package/dist/server-AGHC4VUL.js +0 -11
- package/dist/server-DBUY2KY5.js +0 -11
- package/dist/server-GWBQJPRG.js +0 -11
- package/dist/server-JZZFULVU.js +0 -11
- package/dist/server-L5XAF4M5.js +0 -11
- package/dist/server-NYFUHANA.js +0 -12
- package/dist/server-OABUB2LO.js +0 -12
- package/dist/server-P2CKZWWV.js +0 -12
- package/dist/sprites-XVSMQLSI.js +0 -15
- package/dist/sql-wasm-FORCZFHZ.js +0 -2148
- package/dist/sql-wasm-JZH76RLO.js +0 -2148
- package/dist/sqljs-backend-CJQOIQYE.js +0 -266
- package/dist/sqljs-backend-KWXBUDUO.js +0 -266
- package/dist/state-demo.bundle.js +0 -1033
- package/dist/tenancy-CBWYEVUB.js +0 -11
- package/dist/tenancy-FO6WVYFN.js +0 -13
- package/dist/tenancy-FWDBESMX.js +0 -14
- package/dist/tenancy-HGPIS5QA.js +0 -13
- package/dist/tenancy-HXFALA37.js +0 -14
- package/dist/tenancy-V7ARWNNV.js +0 -14
- package/dist/tenancy-ZDN4GXK5.js +0 -13
- package/dist/tenancy-ZT5VMY7B.js +0 -14
- package/dist/vm-config-YZRJ3KUB.js +0 -21
- /package/dist/{chunk-2BRTCEHB.js → chunk-PZ4XYZ4J.js} +0 -0
- /package/dist/{chunk-EO3J7GRP.js → chunk-WXSWA3MV.js} +0 -0
package/dist/chunk-WMR777PO.js
DELETED
|
@@ -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-NO73OK2I.js";
|
|
12
|
-
import {
|
|
13
|
-
__esm,
|
|
14
|
-
__require
|
|
15
|
-
} from "./chunk-KFQGP6VL.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
|
-
};
|