trellis 3.1.30 → 3.1.33
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 +10 -0
- package/README.md +1 -1
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +320 -24
- package/dist/cli/lane.d.ts +6 -0
- package/dist/cli/lane.d.ts.map +1 -0
- package/dist/cms/client.d.ts +1 -1
- package/dist/cms/client.d.ts.map +1 -1
- package/dist/cms/index.js +0 -16
- package/dist/cms/types.d.ts +1 -1
- package/dist/cms/types.d.ts.map +1 -1
- package/dist/context/manager.d.ts +1 -1
- package/dist/context/manager.d.ts.map +1 -1
- package/dist/context/types.d.ts +1 -1
- package/dist/context/types.d.ts.map +1 -1
- package/dist/decisions/index.js +2 -2
- package/dist/engine.d.ts +86 -2
- package/dist/engine.d.ts.map +1 -1
- package/dist/{index-65z0xfjw.js → index-4cdr7x2x.js} +2 -2
- package/dist/{index-hy73j9z8.js → index-nq520y6k.js} +7 -3
- package/dist/{index-v9b4hqa7.js → index-nzb3am4f.js} +4 -1
- package/dist/{index-ncckgtrk.js → index-rv1p47gp.js} +1000 -114
- package/dist/{index-4gknc19b.js → index-w5wccer3.js} +1 -1
- package/dist/{index-a2a394zz.js → index-xhcp6xrn.js} +503 -41
- package/dist/index.js +39 -7
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/plugins/agent-memory/graph-context-manager.d.ts +5 -0
- package/dist/plugins/agent-memory/graph-context-manager.d.ts.map +1 -1
- package/dist/{remote-manager-n71bmcy1.js → remote-manager-xvbg4230.js} +4 -4
- package/dist/vcs/branch.d.ts +6 -0
- package/dist/vcs/branch.d.ts.map +1 -1
- package/dist/vcs/decompose.d.ts.map +1 -1
- package/dist/vcs/engine-context.d.ts +9 -2
- package/dist/vcs/engine-context.d.ts.map +1 -1
- package/dist/vcs/index.d.ts +2 -0
- package/dist/vcs/index.d.ts.map +1 -1
- package/dist/vcs/index.js +36 -4
- package/dist/vcs/issue.d.ts +2 -0
- package/dist/vcs/issue.d.ts.map +1 -1
- package/dist/vcs/lane-materialize.d.ts +45 -0
- package/dist/vcs/lane-materialize.d.ts.map +1 -0
- package/dist/vcs/lane-promote.d.ts +56 -0
- package/dist/vcs/lane-promote.d.ts.map +1 -0
- package/dist/vcs/lane.d.ts +55 -0
- package/dist/vcs/lane.d.ts.map +1 -0
- package/dist/vcs/op-log.d.ts +29 -0
- package/dist/vcs/op-log.d.ts.map +1 -0
- package/dist/vcs/types.d.ts +15 -1
- package/dist/vcs/types.d.ts.map +1 -1
- package/dist/watcher/ingestion.d.ts +1 -0
- package/dist/watcher/ingestion.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -7,8 +7,9 @@ import {
|
|
|
7
7
|
fileEntityId,
|
|
8
8
|
init_ops,
|
|
9
9
|
init_types,
|
|
10
|
-
issueEntityId
|
|
11
|
-
|
|
10
|
+
issueEntityId,
|
|
11
|
+
laneEntityId
|
|
12
|
+
} from "./index-nzb3am4f.js";
|
|
12
13
|
import {
|
|
13
14
|
__esm,
|
|
14
15
|
__require
|
|
@@ -16,6 +17,22 @@ import {
|
|
|
16
17
|
|
|
17
18
|
// src/vcs/decompose.ts
|
|
18
19
|
import { dirname } from "path";
|
|
20
|
+
function pickFacts(input) {
|
|
21
|
+
if (!Array.isArray(input))
|
|
22
|
+
return [];
|
|
23
|
+
return input.filter((item) => {
|
|
24
|
+
const fact = item;
|
|
25
|
+
return typeof fact.e === "string" && typeof fact.a === "string" && (typeof fact.v === "string" || typeof fact.v === "number" || typeof fact.v === "boolean");
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function pickLinks(input) {
|
|
29
|
+
if (!Array.isArray(input))
|
|
30
|
+
return [];
|
|
31
|
+
return input.filter((item) => {
|
|
32
|
+
const link = item;
|
|
33
|
+
return typeof link.e1 === "string" && typeof link.a === "string" && typeof link.e2 === "string";
|
|
34
|
+
});
|
|
35
|
+
}
|
|
19
36
|
function decompose(op) {
|
|
20
37
|
const result = {
|
|
21
38
|
addFacts: [],
|
|
@@ -230,6 +247,20 @@ function decompose(op) {
|
|
|
230
247
|
v: vcs.issueDescription
|
|
231
248
|
});
|
|
232
249
|
}
|
|
250
|
+
if (vcs.oldParentIssueId) {
|
|
251
|
+
result.deleteLinks.push({
|
|
252
|
+
e1: eid,
|
|
253
|
+
a: "childOf",
|
|
254
|
+
e2: issueEntityId(vcs.oldParentIssueId)
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
if (vcs.parentIssueId) {
|
|
258
|
+
result.addLinks.push({
|
|
259
|
+
e1: eid,
|
|
260
|
+
a: "childOf",
|
|
261
|
+
e2: issueEntityId(vcs.parentIssueId)
|
|
262
|
+
});
|
|
263
|
+
}
|
|
233
264
|
break;
|
|
234
265
|
}
|
|
235
266
|
case "vcs:issueStart": {
|
|
@@ -384,6 +415,102 @@ function decompose(op) {
|
|
|
384
415
|
}
|
|
385
416
|
break;
|
|
386
417
|
}
|
|
418
|
+
case "vcs:laneCreate": {
|
|
419
|
+
if (!vcs.laneId)
|
|
420
|
+
break;
|
|
421
|
+
const lid = laneEntityId(vcs.laneId);
|
|
422
|
+
result.addFacts.push({ e: lid, a: "type", v: "AgentLane" }, { e: lid, a: "status", v: "active" }, { e: lid, a: "createdAt", v: op.timestamp }, { e: lid, a: "createdBy", v: op.agentId });
|
|
423
|
+
if (vcs.baseBranch) {
|
|
424
|
+
result.addFacts.push({ e: lid, a: "baseBranch", v: vcs.baseBranch });
|
|
425
|
+
}
|
|
426
|
+
if (vcs.baseOpHash) {
|
|
427
|
+
result.addFacts.push({ e: lid, a: "baseOpHash", v: vcs.baseOpHash });
|
|
428
|
+
}
|
|
429
|
+
if (vcs.targetBranch) {
|
|
430
|
+
result.addFacts.push({ e: lid, a: "targetBranch", v: vcs.targetBranch });
|
|
431
|
+
}
|
|
432
|
+
if (vcs.baseOpHash) {
|
|
433
|
+
result.addFacts.push({ e: lid, a: "headOpHash", v: vcs.baseOpHash });
|
|
434
|
+
}
|
|
435
|
+
if (vcs.issueId) {
|
|
436
|
+
result.addFacts.push({ e: lid, a: "issueId", v: vcs.issueId });
|
|
437
|
+
}
|
|
438
|
+
if (vcs.sessionId) {
|
|
439
|
+
result.addFacts.push({ e: lid, a: "sessionId", v: vcs.sessionId });
|
|
440
|
+
}
|
|
441
|
+
if (vcs.parentLaneId) {
|
|
442
|
+
result.addFacts.push({ e: lid, a: "parentLaneId", v: vcs.parentLaneId });
|
|
443
|
+
result.addLinks.push({
|
|
444
|
+
e1: lid,
|
|
445
|
+
a: "forkedFrom",
|
|
446
|
+
e2: laneEntityId(vcs.parentLaneId)
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
if (vcs.forkKind) {
|
|
450
|
+
result.addFacts.push({ e: lid, a: "forkKind", v: vcs.forkKind });
|
|
451
|
+
}
|
|
452
|
+
if (vcs.virtualBaseOpHash) {
|
|
453
|
+
result.addFacts.push({
|
|
454
|
+
e: lid,
|
|
455
|
+
a: "virtualBaseOpHash",
|
|
456
|
+
v: vcs.virtualBaseOpHash
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
case "vcs:laneDrop": {
|
|
462
|
+
if (!vcs.laneId)
|
|
463
|
+
break;
|
|
464
|
+
const lid = laneEntityId(vcs.laneId);
|
|
465
|
+
if (vcs.laneStatus) {
|
|
466
|
+
result.deleteFacts.push({ e: lid, a: "status", v: "active" });
|
|
467
|
+
result.addFacts.push({ e: lid, a: "status", v: vcs.laneStatus });
|
|
468
|
+
}
|
|
469
|
+
break;
|
|
470
|
+
}
|
|
471
|
+
case "vcs:lanePromoteStart": {
|
|
472
|
+
if (!vcs.laneId)
|
|
473
|
+
break;
|
|
474
|
+
const lid = laneEntityId(vcs.laneId);
|
|
475
|
+
result.deleteFacts.push({ e: lid, a: "status", v: "active" });
|
|
476
|
+
result.addFacts.push({ e: lid, a: "status", v: "promoting" });
|
|
477
|
+
break;
|
|
478
|
+
}
|
|
479
|
+
case "vcs:lanePromoteComplete": {
|
|
480
|
+
if (!vcs.laneId)
|
|
481
|
+
break;
|
|
482
|
+
const lid = laneEntityId(vcs.laneId);
|
|
483
|
+
result.deleteFacts.push({ e: lid, a: "status", v: "promoting" });
|
|
484
|
+
result.addFacts.push({ e: lid, a: "status", v: "promoted" });
|
|
485
|
+
if (vcs.targetBranch) {
|
|
486
|
+
result.addFacts.push({ e: lid, a: "promotedToBranch", v: vcs.targetBranch });
|
|
487
|
+
}
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
case "vcs:lanePromoteAbort": {
|
|
491
|
+
if (!vcs.laneId)
|
|
492
|
+
break;
|
|
493
|
+
const lid = laneEntityId(vcs.laneId);
|
|
494
|
+
result.deleteFacts.push({ e: lid, a: "status", v: "promoting" });
|
|
495
|
+
result.addFacts.push({ e: lid, a: "status", v: "active" });
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
case "vcs:storeAssert": {
|
|
499
|
+
result.addFacts.push(...pickFacts(vcs.facts));
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
case "vcs:storeRetract": {
|
|
503
|
+
result.deleteFacts.push(...pickFacts(vcs.facts));
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
case "vcs:storeLink": {
|
|
507
|
+
result.addLinks.push(...pickLinks(vcs.links));
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
case "vcs:storeUnlink": {
|
|
511
|
+
result.deleteLinks.push(...pickLinks(vcs.links));
|
|
512
|
+
break;
|
|
513
|
+
}
|
|
387
514
|
}
|
|
388
515
|
return result;
|
|
389
516
|
}
|
|
@@ -474,6 +601,13 @@ var init_blob_store = () => {};
|
|
|
474
601
|
// src/vcs/branch.ts
|
|
475
602
|
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
476
603
|
import { join as join2 } from "path";
|
|
604
|
+
function shouldAdvanceBranchHead(kind) {
|
|
605
|
+
return !BRANCH_ADVANCE_SKIP_KINDS.has(kind);
|
|
606
|
+
}
|
|
607
|
+
function getBranchHeadOpHash(ctx, branchName) {
|
|
608
|
+
const facts = ctx.store.getFactsByEntity(`branch:${branchName}`).filter((f) => f.a === "headOpHash");
|
|
609
|
+
return facts[facts.length - 1]?.v;
|
|
610
|
+
}
|
|
477
611
|
async function createBranch(ctx, name, currentBranch) {
|
|
478
612
|
const existing = ctx.store.getFactsByAttribute("type").filter((f) => f.v === "Branch" && f.e === `branch:${name}`);
|
|
479
613
|
if (existing.length > 0) {
|
|
@@ -488,7 +622,7 @@ async function createBranch(ctx, name, currentBranch) {
|
|
|
488
622
|
targetOpHash: ctx.getLastOp()?.hash
|
|
489
623
|
}
|
|
490
624
|
});
|
|
491
|
-
ctx.applyOp(op);
|
|
625
|
+
await ctx.applyOp(op);
|
|
492
626
|
return op;
|
|
493
627
|
}
|
|
494
628
|
function switchBranch(ctx, name) {
|
|
@@ -523,7 +657,7 @@ async function deleteBranch(ctx, name, currentBranch) {
|
|
|
523
657
|
previousHash: ctx.getLastOp()?.hash,
|
|
524
658
|
vcs: { branchName: name }
|
|
525
659
|
});
|
|
526
|
-
ctx.applyOp(op);
|
|
660
|
+
await ctx.applyOp(op);
|
|
527
661
|
return op;
|
|
528
662
|
}
|
|
529
663
|
function saveBranchState(rootPath, state) {
|
|
@@ -537,14 +671,24 @@ function loadBranchState(rootPath) {
|
|
|
537
671
|
const raw = readFileSync2(statePath, "utf-8");
|
|
538
672
|
const state = JSON.parse(raw);
|
|
539
673
|
if (state.currentBranch) {
|
|
540
|
-
return {
|
|
674
|
+
return {
|
|
675
|
+
currentBranch: state.currentBranch,
|
|
676
|
+
activeLaneId: state.activeLaneId
|
|
677
|
+
};
|
|
541
678
|
}
|
|
542
679
|
} catch {}
|
|
543
680
|
}
|
|
544
681
|
return { currentBranch: "main" };
|
|
545
682
|
}
|
|
683
|
+
var BRANCH_ADVANCE_SKIP_KINDS;
|
|
546
684
|
var init_branch = __esm(() => {
|
|
547
685
|
init_ops();
|
|
686
|
+
BRANCH_ADVANCE_SKIP_KINDS = new Set([
|
|
687
|
+
"vcs:branchAdvance",
|
|
688
|
+
"vcs:branchCreate",
|
|
689
|
+
"vcs:branchDelete",
|
|
690
|
+
"vcs:checkpointCreate"
|
|
691
|
+
]);
|
|
548
692
|
});
|
|
549
693
|
|
|
550
694
|
// src/vcs/milestone.ts
|
|
@@ -583,7 +727,7 @@ async function createMilestone(ctx, message, opts) {
|
|
|
583
727
|
toOpHash
|
|
584
728
|
}
|
|
585
729
|
});
|
|
586
|
-
ctx.applyOp(op);
|
|
730
|
+
await ctx.applyOp(op);
|
|
587
731
|
for (const file of affectedFiles) {
|
|
588
732
|
ctx.store.addFacts([{ e: milestoneId, a: "affectsFile", v: file }]);
|
|
589
733
|
}
|
|
@@ -617,7 +761,7 @@ async function createCheckpoint(ctx, trigger = "manual") {
|
|
|
617
761
|
previousHash: ctx.getLastOp()?.hash,
|
|
618
762
|
vcs: { trigger }
|
|
619
763
|
});
|
|
620
|
-
ctx.applyOp(op);
|
|
764
|
+
await ctx.applyOp(op);
|
|
621
765
|
return op;
|
|
622
766
|
}
|
|
623
767
|
function listCheckpoints(ctx) {
|
|
@@ -1155,25 +1299,56 @@ var init_merge = () => {};
|
|
|
1155
1299
|
// src/vcs/issue.ts
|
|
1156
1300
|
import { exec } from "child_process";
|
|
1157
1301
|
import { promisify } from "util";
|
|
1158
|
-
import {
|
|
1302
|
+
import {
|
|
1303
|
+
existsSync as existsSync3,
|
|
1304
|
+
readFileSync as readFileSync3,
|
|
1305
|
+
writeFileSync as writeFileSync3,
|
|
1306
|
+
mkdirSync as mkdirSync2,
|
|
1307
|
+
openSync,
|
|
1308
|
+
closeSync,
|
|
1309
|
+
unlinkSync
|
|
1310
|
+
} from "fs";
|
|
1159
1311
|
import { join as join3, dirname as dirname2 } from "path";
|
|
1160
1312
|
function getIssueCounterPath(rootPath) {
|
|
1161
1313
|
return join3(rootPath, ".trellis", "issue-counter.json");
|
|
1162
1314
|
}
|
|
1163
1315
|
function nextIssueId(rootPath) {
|
|
1164
1316
|
const counterPath = getIssueCounterPath(rootPath);
|
|
1165
|
-
let counter = 0;
|
|
1166
|
-
if (existsSync3(counterPath)) {
|
|
1167
|
-
try {
|
|
1168
|
-
counter = JSON.parse(readFileSync3(counterPath, "utf-8")).counter ?? 0;
|
|
1169
|
-
} catch {}
|
|
1170
|
-
}
|
|
1171
|
-
counter++;
|
|
1172
1317
|
const dir = dirname2(counterPath);
|
|
1173
1318
|
if (!existsSync3(dir))
|
|
1174
1319
|
mkdirSync2(dir, { recursive: true });
|
|
1175
|
-
|
|
1176
|
-
|
|
1320
|
+
const lockPath = `${counterPath}.lock`;
|
|
1321
|
+
const deadline = Date.now() + 5000;
|
|
1322
|
+
let lockFd;
|
|
1323
|
+
while (Date.now() < deadline) {
|
|
1324
|
+
try {
|
|
1325
|
+
lockFd = openSync(lockPath, "wx");
|
|
1326
|
+
break;
|
|
1327
|
+
} catch (err) {
|
|
1328
|
+
if (err?.code !== "EEXIST") {
|
|
1329
|
+
throw err;
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
if (lockFd === undefined) {
|
|
1334
|
+
throw new Error(`Timed out waiting for issue counter lock: ${lockPath}. Another Trellis process may be stalled.`);
|
|
1335
|
+
}
|
|
1336
|
+
try {
|
|
1337
|
+
let counter = 0;
|
|
1338
|
+
if (existsSync3(counterPath)) {
|
|
1339
|
+
try {
|
|
1340
|
+
counter = JSON.parse(readFileSync3(counterPath, "utf-8")).counter ?? 0;
|
|
1341
|
+
} catch {}
|
|
1342
|
+
}
|
|
1343
|
+
counter++;
|
|
1344
|
+
writeFileSync3(counterPath, JSON.stringify({ counter }, null, 2));
|
|
1345
|
+
return `TRL-${counter}`;
|
|
1346
|
+
} finally {
|
|
1347
|
+
closeSync(lockFd);
|
|
1348
|
+
try {
|
|
1349
|
+
unlinkSync(lockPath);
|
|
1350
|
+
} catch {}
|
|
1351
|
+
}
|
|
1177
1352
|
}
|
|
1178
1353
|
function getIssueFact(ctx, entityId, attr) {
|
|
1179
1354
|
const facts = ctx.store.getFactsByEntity(entityId);
|
|
@@ -1263,7 +1438,7 @@ async function createIssue(ctx, rootPath, title, opts) {
|
|
|
1263
1438
|
parentIssueId: opts?.parentId
|
|
1264
1439
|
}
|
|
1265
1440
|
});
|
|
1266
|
-
ctx.applyOp(op);
|
|
1441
|
+
await ctx.applyOp(op);
|
|
1267
1442
|
if (opts?.criteria) {
|
|
1268
1443
|
for (let i = 0;i < opts.criteria.length; i++) {
|
|
1269
1444
|
await addCriterion(ctx, id, opts.criteria[i].description, opts.criteria[i].command);
|
|
@@ -1272,21 +1447,50 @@ async function createIssue(ctx, rootPath, title, opts) {
|
|
|
1272
1447
|
return op;
|
|
1273
1448
|
}
|
|
1274
1449
|
async function updateIssue(ctx, id, updates) {
|
|
1450
|
+
const issue = getIssue(ctx, id);
|
|
1451
|
+
if (!issue) {
|
|
1452
|
+
throw new Error(`Issue ${id} not found.`);
|
|
1453
|
+
}
|
|
1454
|
+
const vcs = { issueId: id };
|
|
1455
|
+
if (updates.title !== undefined)
|
|
1456
|
+
vcs.issueTitle = updates.title;
|
|
1457
|
+
if (updates.description !== undefined)
|
|
1458
|
+
vcs.issueDescription = updates.description;
|
|
1459
|
+
if (updates.status !== undefined) {
|
|
1460
|
+
vcs.oldIssueStatus = getIssueFact(ctx, issueEntityId(id), "status");
|
|
1461
|
+
vcs.issueStatus = updates.status;
|
|
1462
|
+
}
|
|
1463
|
+
if (updates.priority !== undefined)
|
|
1464
|
+
vcs.issuePriority = updates.priority;
|
|
1465
|
+
if (updates.labels !== undefined)
|
|
1466
|
+
vcs.issueLabels = updates.labels;
|
|
1467
|
+
if (updates.assignee !== undefined)
|
|
1468
|
+
vcs.issueAssignee = updates.assignee;
|
|
1469
|
+
if (updates.parentId !== undefined) {
|
|
1470
|
+
const newParent = updates.parentId;
|
|
1471
|
+
if (newParent === id) {
|
|
1472
|
+
throw new Error(`Issue ${id} cannot be its own parent.`);
|
|
1473
|
+
}
|
|
1474
|
+
if (newParent) {
|
|
1475
|
+
const parent = getIssue(ctx, newParent);
|
|
1476
|
+
if (!parent) {
|
|
1477
|
+
throw new Error(`Parent issue ${newParent} not found.`);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
const currentParent = issue.parentId;
|
|
1481
|
+
if (newParent !== currentParent) {
|
|
1482
|
+
if (currentParent)
|
|
1483
|
+
vcs.oldParentIssueId = currentParent;
|
|
1484
|
+
if (newParent)
|
|
1485
|
+
vcs.parentIssueId = newParent;
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1275
1488
|
const op = await createVcsOp("vcs:issueUpdate", {
|
|
1276
1489
|
agentId: ctx.agentId,
|
|
1277
1490
|
previousHash: ctx.getLastOp()?.hash,
|
|
1278
|
-
vcs
|
|
1279
|
-
issueId: id,
|
|
1280
|
-
issueTitle: updates.title,
|
|
1281
|
-
issueDescription: updates.description,
|
|
1282
|
-
oldIssueStatus: updates.status ? getIssueFact(ctx, issueEntityId(id), "status") : undefined,
|
|
1283
|
-
issueStatus: updates.status,
|
|
1284
|
-
issuePriority: updates.priority,
|
|
1285
|
-
issueLabels: updates.labels,
|
|
1286
|
-
issueAssignee: updates.assignee
|
|
1287
|
-
}
|
|
1491
|
+
vcs
|
|
1288
1492
|
});
|
|
1289
|
-
ctx.applyOp(op);
|
|
1493
|
+
await ctx.applyOp(op);
|
|
1290
1494
|
return op;
|
|
1291
1495
|
}
|
|
1292
1496
|
async function startIssue(ctx, id, branchName) {
|
|
@@ -1308,7 +1512,7 @@ async function startIssue(ctx, id, branchName) {
|
|
|
1308
1512
|
branchName
|
|
1309
1513
|
}
|
|
1310
1514
|
});
|
|
1311
|
-
ctx.applyOp(op);
|
|
1515
|
+
await ctx.applyOp(op);
|
|
1312
1516
|
return op;
|
|
1313
1517
|
}
|
|
1314
1518
|
async function pauseIssue(ctx, id, note) {
|
|
@@ -1329,7 +1533,7 @@ async function pauseIssue(ctx, id, note) {
|
|
|
1329
1533
|
pauseNote: note.trim()
|
|
1330
1534
|
}
|
|
1331
1535
|
});
|
|
1332
|
-
ctx.applyOp(op);
|
|
1536
|
+
await ctx.applyOp(op);
|
|
1333
1537
|
return op;
|
|
1334
1538
|
}
|
|
1335
1539
|
async function resumeIssue(ctx, id) {
|
|
@@ -1343,7 +1547,7 @@ async function resumeIssue(ctx, id) {
|
|
|
1343
1547
|
previousHash: ctx.getLastOp()?.hash,
|
|
1344
1548
|
vcs: { issueId: id, oldIssueStatus: status }
|
|
1345
1549
|
});
|
|
1346
|
-
ctx.applyOp(op);
|
|
1550
|
+
await ctx.applyOp(op);
|
|
1347
1551
|
return op;
|
|
1348
1552
|
}
|
|
1349
1553
|
async function closeIssue(ctx, id, opts) {
|
|
@@ -1375,7 +1579,7 @@ async function closeIssue(ctx, id, opts) {
|
|
|
1375
1579
|
previousHash: ctx.getLastOp()?.hash,
|
|
1376
1580
|
vcs: { issueId: id, oldIssueStatus: status }
|
|
1377
1581
|
});
|
|
1378
|
-
ctx.applyOp(op);
|
|
1582
|
+
await ctx.applyOp(op);
|
|
1379
1583
|
if (startedAt) {
|
|
1380
1584
|
const durationMs = Date.now() - new Date(startedAt).getTime();
|
|
1381
1585
|
ctx.store.addFacts([{ e: eid, a: "durationMs", v: durationMs }]);
|
|
@@ -1397,7 +1601,7 @@ async function triageIssue(ctx, id) {
|
|
|
1397
1601
|
issueStatus: "queue"
|
|
1398
1602
|
}
|
|
1399
1603
|
});
|
|
1400
|
-
ctx.applyOp(op);
|
|
1604
|
+
await ctx.applyOp(op);
|
|
1401
1605
|
return op;
|
|
1402
1606
|
}
|
|
1403
1607
|
async function reopenIssue(ctx, id) {
|
|
@@ -1411,7 +1615,7 @@ async function reopenIssue(ctx, id) {
|
|
|
1411
1615
|
previousHash: ctx.getLastOp()?.hash,
|
|
1412
1616
|
vcs: { issueId: id, oldIssueStatus: status }
|
|
1413
1617
|
});
|
|
1414
|
-
ctx.applyOp(op);
|
|
1618
|
+
await ctx.applyOp(op);
|
|
1415
1619
|
return op;
|
|
1416
1620
|
}
|
|
1417
1621
|
async function assignIssue(ctx, id, agentId) {
|
|
@@ -1434,7 +1638,7 @@ async function blockIssue(ctx, id, blockedById) {
|
|
|
1434
1638
|
previousHash: ctx.getLastOp()?.hash,
|
|
1435
1639
|
vcs: { issueId: id, blockedByIssueId: blockedById }
|
|
1436
1640
|
});
|
|
1437
|
-
ctx.applyOp(op);
|
|
1641
|
+
await ctx.applyOp(op);
|
|
1438
1642
|
return op;
|
|
1439
1643
|
}
|
|
1440
1644
|
async function unblockIssue(ctx, id, blockedById) {
|
|
@@ -1443,7 +1647,7 @@ async function unblockIssue(ctx, id, blockedById) {
|
|
|
1443
1647
|
previousHash: ctx.getLastOp()?.hash,
|
|
1444
1648
|
vcs: { issueId: id, blockedByIssueId: blockedById }
|
|
1445
1649
|
});
|
|
1446
|
-
ctx.applyOp(op);
|
|
1650
|
+
await ctx.applyOp(op);
|
|
1447
1651
|
return op;
|
|
1448
1652
|
}
|
|
1449
1653
|
async function addCriterion(ctx, issueId, description, command) {
|
|
@@ -1460,7 +1664,7 @@ async function addCriterion(ctx, issueId, description, command) {
|
|
|
1460
1664
|
criterionCommand: command
|
|
1461
1665
|
}
|
|
1462
1666
|
});
|
|
1463
|
-
ctx.applyOp(op);
|
|
1667
|
+
await ctx.applyOp(op);
|
|
1464
1668
|
return op;
|
|
1465
1669
|
}
|
|
1466
1670
|
async function setCriterionStatus(ctx, issueId, criterionIndex, status) {
|
|
@@ -1478,7 +1682,7 @@ async function setCriterionStatus(ctx, issueId, criterionIndex, status) {
|
|
|
1478
1682
|
criterionStatus: status
|
|
1479
1683
|
}
|
|
1480
1684
|
});
|
|
1481
|
-
ctx.applyOp(op);
|
|
1685
|
+
await ctx.applyOp(op);
|
|
1482
1686
|
return op;
|
|
1483
1687
|
}
|
|
1484
1688
|
async function runCriteria(ctx, issueId, rootPath) {
|
|
@@ -1521,7 +1725,7 @@ async function runCriteria(ctx, issueId, rootPath) {
|
|
|
1521
1725
|
criterionOutput: output.slice(0, 4096)
|
|
1522
1726
|
}
|
|
1523
1727
|
});
|
|
1524
|
-
ctx.applyOp(updateOp);
|
|
1728
|
+
await ctx.applyOp(updateOp);
|
|
1525
1729
|
results.push({
|
|
1526
1730
|
id: c.id,
|
|
1527
1731
|
description: c.description,
|
|
@@ -1594,4 +1798,262 @@ var init_issue = __esm(() => {
|
|
|
1594
1798
|
execAsync = promisify(exec);
|
|
1595
1799
|
});
|
|
1596
1800
|
|
|
1597
|
-
|
|
1801
|
+
// src/vcs/op-log.ts
|
|
1802
|
+
import {
|
|
1803
|
+
existsSync as existsSync4,
|
|
1804
|
+
mkdirSync as mkdirSync3,
|
|
1805
|
+
readFileSync as readFileSync4,
|
|
1806
|
+
writeFileSync as writeFileSync4,
|
|
1807
|
+
copyFileSync,
|
|
1808
|
+
openSync as openSync2,
|
|
1809
|
+
closeSync as closeSync2,
|
|
1810
|
+
unlinkSync as unlinkSync2,
|
|
1811
|
+
renameSync
|
|
1812
|
+
} from "fs";
|
|
1813
|
+
import { dirname as dirname3 } from "path";
|
|
1814
|
+
function lockTimeoutMs() {
|
|
1815
|
+
const raw = process.env.TRELLIS_OPLOG_LOCK_MS;
|
|
1816
|
+
if (!raw)
|
|
1817
|
+
return 5000;
|
|
1818
|
+
const parsed = Number(raw);
|
|
1819
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 5000;
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
class JsonOpLog {
|
|
1823
|
+
ops = [];
|
|
1824
|
+
filePath;
|
|
1825
|
+
lockPath;
|
|
1826
|
+
constructor(filePath) {
|
|
1827
|
+
this.filePath = filePath;
|
|
1828
|
+
this.lockPath = `${filePath}.lock`;
|
|
1829
|
+
}
|
|
1830
|
+
get path() {
|
|
1831
|
+
return this.filePath;
|
|
1832
|
+
}
|
|
1833
|
+
load() {
|
|
1834
|
+
if (existsSync4(this.filePath)) {
|
|
1835
|
+
const raw = readFileSync4(this.filePath, "utf-8");
|
|
1836
|
+
try {
|
|
1837
|
+
this.ops = JSON.parse(raw);
|
|
1838
|
+
} catch {
|
|
1839
|
+
const backupPath = this.filePath + ".bak";
|
|
1840
|
+
if (existsSync4(backupPath)) {
|
|
1841
|
+
const backupRaw = readFileSync4(backupPath, "utf-8");
|
|
1842
|
+
this.ops = JSON.parse(backupRaw);
|
|
1843
|
+
writeFileSync4(this.filePath, backupRaw);
|
|
1844
|
+
} else {
|
|
1845
|
+
throw new Error(`Corrupted ops log at ${this.filePath} and no backup found. Run \`trellis repair\` to attempt recovery.`);
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
append(op) {
|
|
1851
|
+
this.withLock(() => {
|
|
1852
|
+
const diskOps = this.readOpsFromDisk();
|
|
1853
|
+
this.ops = diskOps;
|
|
1854
|
+
if (this.ops.some((existing) => existing.hash === op.hash)) {
|
|
1855
|
+
return;
|
|
1856
|
+
}
|
|
1857
|
+
this.ops.push(op);
|
|
1858
|
+
this.flush();
|
|
1859
|
+
});
|
|
1860
|
+
}
|
|
1861
|
+
readAll() {
|
|
1862
|
+
return [...this.ops];
|
|
1863
|
+
}
|
|
1864
|
+
getLastOp() {
|
|
1865
|
+
return this.ops.length > 0 ? this.ops[this.ops.length - 1] : undefined;
|
|
1866
|
+
}
|
|
1867
|
+
count() {
|
|
1868
|
+
return this.ops.length;
|
|
1869
|
+
}
|
|
1870
|
+
flush() {
|
|
1871
|
+
const dir = dirname3(this.filePath);
|
|
1872
|
+
if (!existsSync4(dir))
|
|
1873
|
+
mkdirSync3(dir, { recursive: true });
|
|
1874
|
+
if (existsSync4(this.filePath)) {
|
|
1875
|
+
const backupPath = this.filePath + ".bak";
|
|
1876
|
+
try {
|
|
1877
|
+
copyFileSync(this.filePath, backupPath);
|
|
1878
|
+
} catch {}
|
|
1879
|
+
}
|
|
1880
|
+
const tempPath = `${this.filePath}.tmp`;
|
|
1881
|
+
writeFileSync4(tempPath, JSON.stringify(this.ops, null, 2));
|
|
1882
|
+
renameSync(tempPath, this.filePath);
|
|
1883
|
+
}
|
|
1884
|
+
readOpsFromDisk() {
|
|
1885
|
+
if (!existsSync4(this.filePath)) {
|
|
1886
|
+
return [];
|
|
1887
|
+
}
|
|
1888
|
+
const raw = readFileSync4(this.filePath, "utf-8");
|
|
1889
|
+
try {
|
|
1890
|
+
const parsed = JSON.parse(raw);
|
|
1891
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
1892
|
+
} catch {
|
|
1893
|
+
const backupPath = this.filePath + ".bak";
|
|
1894
|
+
if (existsSync4(backupPath)) {
|
|
1895
|
+
const backupRaw = readFileSync4(backupPath, "utf-8");
|
|
1896
|
+
const parsedBackup = JSON.parse(backupRaw);
|
|
1897
|
+
writeFileSync4(this.filePath, backupRaw);
|
|
1898
|
+
return Array.isArray(parsedBackup) ? parsedBackup : [];
|
|
1899
|
+
}
|
|
1900
|
+
throw new Error(`Corrupted ops log at ${this.filePath} and no backup found. Run \`trellis repair\` to attempt recovery.`);
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
withLock(fn) {
|
|
1904
|
+
const dir = dirname3(this.filePath);
|
|
1905
|
+
if (!existsSync4(dir))
|
|
1906
|
+
mkdirSync3(dir, { recursive: true });
|
|
1907
|
+
const deadline = Date.now() + lockTimeoutMs();
|
|
1908
|
+
let lockFd;
|
|
1909
|
+
while (Date.now() < deadline) {
|
|
1910
|
+
try {
|
|
1911
|
+
lockFd = openSync2(this.lockPath, "wx");
|
|
1912
|
+
break;
|
|
1913
|
+
} catch (err) {
|
|
1914
|
+
if (err?.code !== "EEXIST") {
|
|
1915
|
+
throw err;
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
if (lockFd === undefined) {
|
|
1920
|
+
throw new Error(`Timed out waiting for ops log lock: ${this.lockPath}. Another Trellis process may be stalled.`);
|
|
1921
|
+
}
|
|
1922
|
+
try {
|
|
1923
|
+
fn();
|
|
1924
|
+
} finally {
|
|
1925
|
+
closeSync2(lockFd);
|
|
1926
|
+
try {
|
|
1927
|
+
unlinkSync2(this.lockPath);
|
|
1928
|
+
} catch {}
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
static repair(filePath) {
|
|
1932
|
+
if (!existsSync4(filePath)) {
|
|
1933
|
+
return { recovered: 0, lost: 0 };
|
|
1934
|
+
}
|
|
1935
|
+
const raw = readFileSync4(filePath, "utf-8");
|
|
1936
|
+
try {
|
|
1937
|
+
const ops = JSON.parse(raw);
|
|
1938
|
+
return { recovered: ops.length, lost: 0 };
|
|
1939
|
+
} catch {}
|
|
1940
|
+
const lastHash = raw.lastIndexOf('"hash": "trellis:op:');
|
|
1941
|
+
if (lastHash === -1) {
|
|
1942
|
+
const bakPath = filePath + ".bak";
|
|
1943
|
+
if (existsSync4(bakPath)) {
|
|
1944
|
+
const bakRaw = readFileSync4(bakPath, "utf-8");
|
|
1945
|
+
try {
|
|
1946
|
+
const ops = JSON.parse(bakRaw);
|
|
1947
|
+
writeFileSync4(filePath, bakRaw);
|
|
1948
|
+
return { recovered: ops.length, lost: 0 };
|
|
1949
|
+
} catch {}
|
|
1950
|
+
}
|
|
1951
|
+
writeFileSync4(filePath, "[]");
|
|
1952
|
+
return { recovered: 0, lost: -1 };
|
|
1953
|
+
}
|
|
1954
|
+
const endOfLine = raw.indexOf(`
|
|
1955
|
+
`, lastHash);
|
|
1956
|
+
const closingBrace = raw.indexOf(" }", endOfLine);
|
|
1957
|
+
if (closingBrace === -1) {
|
|
1958
|
+
writeFileSync4(filePath, "[]");
|
|
1959
|
+
return { recovered: 0, lost: -1 };
|
|
1960
|
+
}
|
|
1961
|
+
const fixed = raw.slice(0, closingBrace + 3) + `
|
|
1962
|
+
]`;
|
|
1963
|
+
try {
|
|
1964
|
+
const ops = JSON.parse(fixed);
|
|
1965
|
+
writeFileSync4(filePath + ".corrupted", raw);
|
|
1966
|
+
writeFileSync4(filePath, fixed);
|
|
1967
|
+
return { recovered: ops.length, lost: 0 };
|
|
1968
|
+
} catch {
|
|
1969
|
+
writeFileSync4(filePath + ".corrupted", raw);
|
|
1970
|
+
writeFileSync4(filePath, "[]");
|
|
1971
|
+
return { recovered: 0, lost: -1 };
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
var LaneOpLog;
|
|
1976
|
+
var init_op_log = __esm(() => {
|
|
1977
|
+
LaneOpLog = class LaneOpLog extends JsonOpLog {
|
|
1978
|
+
constructor(laneDir) {
|
|
1979
|
+
super(`${laneDir}/ops.json`);
|
|
1980
|
+
}
|
|
1981
|
+
};
|
|
1982
|
+
});
|
|
1983
|
+
|
|
1984
|
+
// src/vcs/lane.ts
|
|
1985
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync5, readdirSync } from "fs";
|
|
1986
|
+
import { join as join4 } from "path";
|
|
1987
|
+
import { randomUUID } from "crypto";
|
|
1988
|
+
function lanesRoot(trellisDir) {
|
|
1989
|
+
return join4(trellisDir, "lanes");
|
|
1990
|
+
}
|
|
1991
|
+
function laneDir(trellisDir, laneId) {
|
|
1992
|
+
return join4(lanesRoot(trellisDir), laneId);
|
|
1993
|
+
}
|
|
1994
|
+
function laneMetaPath(trellisDir, laneId) {
|
|
1995
|
+
return join4(laneDir(trellisDir, laneId), "meta.json");
|
|
1996
|
+
}
|
|
1997
|
+
function newLaneId() {
|
|
1998
|
+
return `lane-${randomUUID()}`;
|
|
1999
|
+
}
|
|
2000
|
+
function loadLaneMeta(trellisDir, laneId) {
|
|
2001
|
+
const path = laneMetaPath(trellisDir, laneId);
|
|
2002
|
+
if (!existsSync5(path))
|
|
2003
|
+
return;
|
|
2004
|
+
return JSON.parse(readFileSync5(path, "utf-8"));
|
|
2005
|
+
}
|
|
2006
|
+
function saveLaneMeta(trellisDir, meta) {
|
|
2007
|
+
const dir = laneDir(trellisDir, meta.id);
|
|
2008
|
+
if (!existsSync5(dir))
|
|
2009
|
+
mkdirSync4(dir, { recursive: true });
|
|
2010
|
+
writeFileSync5(laneMetaPath(trellisDir, meta.id), JSON.stringify(meta, null, 2) + `
|
|
2011
|
+
`);
|
|
2012
|
+
}
|
|
2013
|
+
function listLaneIds(trellisDir) {
|
|
2014
|
+
const root = lanesRoot(trellisDir);
|
|
2015
|
+
if (!existsSync5(root))
|
|
2016
|
+
return [];
|
|
2017
|
+
return readdirSync(root, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.startsWith("lane-")).map((d) => d.name);
|
|
2018
|
+
}
|
|
2019
|
+
function resolveLaneHeadFromJournal(meta, laneOps) {
|
|
2020
|
+
return laneOps.at(-1)?.hash ?? meta.headOpHash ?? meta.baseOpHash;
|
|
2021
|
+
}
|
|
2022
|
+
function createLaneMeta(trellisDir, params) {
|
|
2023
|
+
const now = new Date().toISOString();
|
|
2024
|
+
const meta = {
|
|
2025
|
+
id: newLaneId(),
|
|
2026
|
+
status: "active",
|
|
2027
|
+
baseBranch: params.baseBranch,
|
|
2028
|
+
baseOpHash: params.baseOpHash,
|
|
2029
|
+
targetBranch: params.targetBranch ?? params.baseBranch,
|
|
2030
|
+
headOpHash: params.baseOpHash,
|
|
2031
|
+
agentId: params.agentId,
|
|
2032
|
+
issueId: params.issueId,
|
|
2033
|
+
sessionId: params.sessionId,
|
|
2034
|
+
parentLaneId: params.parentLaneId,
|
|
2035
|
+
forkKind: params.forkKind,
|
|
2036
|
+
forkedAt: params.forkedAt,
|
|
2037
|
+
virtualBaseOpHash: params.virtualBaseOpHash,
|
|
2038
|
+
worktreePath: params.worktreePath,
|
|
2039
|
+
leaseExpiresAt: params.leaseExpiresAt,
|
|
2040
|
+
createdAt: now,
|
|
2041
|
+
updatedAt: now
|
|
2042
|
+
};
|
|
2043
|
+
saveLaneMeta(trellisDir, meta);
|
|
2044
|
+
return meta;
|
|
2045
|
+
}
|
|
2046
|
+
function updateLaneHead(trellisDir, laneId, headOpHash) {
|
|
2047
|
+
const meta = loadLaneMeta(trellisDir, laneId);
|
|
2048
|
+
if (!meta)
|
|
2049
|
+
return;
|
|
2050
|
+
meta.headOpHash = headOpHash;
|
|
2051
|
+
meta.updatedAt = new Date().toISOString();
|
|
2052
|
+
saveLaneMeta(trellisDir, meta);
|
|
2053
|
+
}
|
|
2054
|
+
function listLaneMetas(trellisDir) {
|
|
2055
|
+
return listLaneIds(trellisDir).map((id) => loadLaneMeta(trellisDir, id)).filter((m) => m !== undefined);
|
|
2056
|
+
}
|
|
2057
|
+
var init_lane = () => {};
|
|
2058
|
+
|
|
2059
|
+
export { decompose, init_decompose, BlobStore, init_blob_store, shouldAdvanceBranchHead, getBranchHeadOpHash, createBranch, switchBranch, listBranches, deleteBranch, saveBranchState, loadBranchState, init_branch, createMilestone, listMilestones, init_milestone, createCheckpoint, listCheckpoints, init_checkpoint, diffFileStates, buildFileStateAtOp, diffOpRange, generateUnifiedDiff, myersDiff, init_diff, threeWayMerge, threeWayTextMerge, init_merge, createIssue, updateIssue, startIssue, pauseIssue, resumeIssue, closeIssue, triageIssue, reopenIssue, assignIssue, blockIssue, unblockIssue, addCriterion, setCriterionStatus, runCriteria, listIssues, getIssue, getActiveIssues, checkCompletionReadiness, init_issue, JsonOpLog, LaneOpLog, init_op_log, lanesRoot, laneDir, laneMetaPath, newLaneId, loadLaneMeta, saveLaneMeta, listLaneIds, resolveLaneHeadFromJournal, createLaneMeta, updateLaneHead, listLaneMetas, init_lane };
|