trellis 3.1.31 → 3.1.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +1 -1
  3. package/dist/cli/index.d.ts +1 -0
  4. package/dist/cli/index.d.ts.map +1 -1
  5. package/dist/cli/index.js +320 -24
  6. package/dist/cli/lane.d.ts +6 -0
  7. package/dist/cli/lane.d.ts.map +1 -0
  8. package/dist/cms/client.d.ts +1 -1
  9. package/dist/cms/client.d.ts.map +1 -1
  10. package/dist/cms/index.js +0 -16
  11. package/dist/cms/types.d.ts +1 -1
  12. package/dist/cms/types.d.ts.map +1 -1
  13. package/dist/context/manager.d.ts +1 -1
  14. package/dist/context/manager.d.ts.map +1 -1
  15. package/dist/context/types.d.ts +1 -1
  16. package/dist/context/types.d.ts.map +1 -1
  17. package/dist/decisions/index.js +2 -2
  18. package/dist/engine.d.ts +86 -2
  19. package/dist/engine.d.ts.map +1 -1
  20. package/dist/{index-65z0xfjw.js → index-4cdr7x2x.js} +2 -2
  21. package/dist/{index-r5064492.js → index-nq520y6k.js} +7 -3
  22. package/dist/{index-v9b4hqa7.js → index-nzb3am4f.js} +4 -1
  23. package/dist/{index-8f2z3b0h.js → index-rv1p47gp.js} +996 -172
  24. package/dist/{index-1tv9sbwp.js → index-w5wccer3.js} +1 -1
  25. package/dist/{index-p3nnc7ac.js → index-xhcp6xrn.js} +462 -31
  26. package/dist/index.js +39 -7
  27. package/dist/mcp/server.d.ts.map +1 -1
  28. package/dist/plugins/agent-memory/graph-context-manager.d.ts +5 -0
  29. package/dist/plugins/agent-memory/graph-context-manager.d.ts.map +1 -1
  30. package/dist/{remote-manager-8qbz3mrn.js → remote-manager-xvbg4230.js} +4 -4
  31. package/dist/vcs/branch.d.ts +6 -0
  32. package/dist/vcs/branch.d.ts.map +1 -1
  33. package/dist/vcs/decompose.d.ts.map +1 -1
  34. package/dist/vcs/engine-context.d.ts +9 -2
  35. package/dist/vcs/engine-context.d.ts.map +1 -1
  36. package/dist/vcs/index.d.ts +2 -0
  37. package/dist/vcs/index.d.ts.map +1 -1
  38. package/dist/vcs/index.js +36 -4
  39. package/dist/vcs/issue.d.ts +2 -0
  40. package/dist/vcs/issue.d.ts.map +1 -1
  41. package/dist/vcs/lane-materialize.d.ts +45 -0
  42. package/dist/vcs/lane-materialize.d.ts.map +1 -0
  43. package/dist/vcs/lane-promote.d.ts +56 -0
  44. package/dist/vcs/lane-promote.d.ts.map +1 -0
  45. package/dist/vcs/lane.d.ts +55 -0
  46. package/dist/vcs/lane.d.ts.map +1 -0
  47. package/dist/vcs/op-log.d.ts +29 -0
  48. package/dist/vcs/op-log.d.ts.map +1 -0
  49. package/dist/vcs/types.d.ts +15 -1
  50. package/dist/vcs/types.d.ts.map +1 -1
  51. package/dist/watcher/ingestion.d.ts +1 -0
  52. package/dist/watcher/ingestion.d.ts.map +1 -1
  53. package/package.json +1 -1
@@ -5,7 +5,7 @@ import {
5
5
  init_profile,
6
6
  init_write,
7
7
  loadProfile
8
- } from "./index-8f2z3b0h.js";
8
+ } from "./index-rv1p47gp.js";
9
9
 
10
10
  // src/scaffold/seed.ts
11
11
  init_profile();
@@ -7,8 +7,9 @@ import {
7
7
  fileEntityId,
8
8
  init_ops,
9
9
  init_types,
10
- issueEntityId
11
- } from "./index-v9b4hqa7.js";
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 { currentBranch: state.currentBranch };
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) {
@@ -1294,7 +1438,7 @@ async function createIssue(ctx, rootPath, title, opts) {
1294
1438
  parentIssueId: opts?.parentId
1295
1439
  }
1296
1440
  });
1297
- ctx.applyOp(op);
1441
+ await ctx.applyOp(op);
1298
1442
  if (opts?.criteria) {
1299
1443
  for (let i = 0;i < opts.criteria.length; i++) {
1300
1444
  await addCriterion(ctx, id, opts.criteria[i].description, opts.criteria[i].command);
@@ -1303,21 +1447,50 @@ async function createIssue(ctx, rootPath, title, opts) {
1303
1447
  return op;
1304
1448
  }
1305
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
+ }
1306
1488
  const op = await createVcsOp("vcs:issueUpdate", {
1307
1489
  agentId: ctx.agentId,
1308
1490
  previousHash: ctx.getLastOp()?.hash,
1309
- vcs: {
1310
- issueId: id,
1311
- issueTitle: updates.title,
1312
- issueDescription: updates.description,
1313
- oldIssueStatus: updates.status ? getIssueFact(ctx, issueEntityId(id), "status") : undefined,
1314
- issueStatus: updates.status,
1315
- issuePriority: updates.priority,
1316
- issueLabels: updates.labels,
1317
- issueAssignee: updates.assignee
1318
- }
1491
+ vcs
1319
1492
  });
1320
- ctx.applyOp(op);
1493
+ await ctx.applyOp(op);
1321
1494
  return op;
1322
1495
  }
1323
1496
  async function startIssue(ctx, id, branchName) {
@@ -1339,7 +1512,7 @@ async function startIssue(ctx, id, branchName) {
1339
1512
  branchName
1340
1513
  }
1341
1514
  });
1342
- ctx.applyOp(op);
1515
+ await ctx.applyOp(op);
1343
1516
  return op;
1344
1517
  }
1345
1518
  async function pauseIssue(ctx, id, note) {
@@ -1360,7 +1533,7 @@ async function pauseIssue(ctx, id, note) {
1360
1533
  pauseNote: note.trim()
1361
1534
  }
1362
1535
  });
1363
- ctx.applyOp(op);
1536
+ await ctx.applyOp(op);
1364
1537
  return op;
1365
1538
  }
1366
1539
  async function resumeIssue(ctx, id) {
@@ -1374,7 +1547,7 @@ async function resumeIssue(ctx, id) {
1374
1547
  previousHash: ctx.getLastOp()?.hash,
1375
1548
  vcs: { issueId: id, oldIssueStatus: status }
1376
1549
  });
1377
- ctx.applyOp(op);
1550
+ await ctx.applyOp(op);
1378
1551
  return op;
1379
1552
  }
1380
1553
  async function closeIssue(ctx, id, opts) {
@@ -1406,7 +1579,7 @@ async function closeIssue(ctx, id, opts) {
1406
1579
  previousHash: ctx.getLastOp()?.hash,
1407
1580
  vcs: { issueId: id, oldIssueStatus: status }
1408
1581
  });
1409
- ctx.applyOp(op);
1582
+ await ctx.applyOp(op);
1410
1583
  if (startedAt) {
1411
1584
  const durationMs = Date.now() - new Date(startedAt).getTime();
1412
1585
  ctx.store.addFacts([{ e: eid, a: "durationMs", v: durationMs }]);
@@ -1428,7 +1601,7 @@ async function triageIssue(ctx, id) {
1428
1601
  issueStatus: "queue"
1429
1602
  }
1430
1603
  });
1431
- ctx.applyOp(op);
1604
+ await ctx.applyOp(op);
1432
1605
  return op;
1433
1606
  }
1434
1607
  async function reopenIssue(ctx, id) {
@@ -1442,7 +1615,7 @@ async function reopenIssue(ctx, id) {
1442
1615
  previousHash: ctx.getLastOp()?.hash,
1443
1616
  vcs: { issueId: id, oldIssueStatus: status }
1444
1617
  });
1445
- ctx.applyOp(op);
1618
+ await ctx.applyOp(op);
1446
1619
  return op;
1447
1620
  }
1448
1621
  async function assignIssue(ctx, id, agentId) {
@@ -1465,7 +1638,7 @@ async function blockIssue(ctx, id, blockedById) {
1465
1638
  previousHash: ctx.getLastOp()?.hash,
1466
1639
  vcs: { issueId: id, blockedByIssueId: blockedById }
1467
1640
  });
1468
- ctx.applyOp(op);
1641
+ await ctx.applyOp(op);
1469
1642
  return op;
1470
1643
  }
1471
1644
  async function unblockIssue(ctx, id, blockedById) {
@@ -1474,7 +1647,7 @@ async function unblockIssue(ctx, id, blockedById) {
1474
1647
  previousHash: ctx.getLastOp()?.hash,
1475
1648
  vcs: { issueId: id, blockedByIssueId: blockedById }
1476
1649
  });
1477
- ctx.applyOp(op);
1650
+ await ctx.applyOp(op);
1478
1651
  return op;
1479
1652
  }
1480
1653
  async function addCriterion(ctx, issueId, description, command) {
@@ -1491,7 +1664,7 @@ async function addCriterion(ctx, issueId, description, command) {
1491
1664
  criterionCommand: command
1492
1665
  }
1493
1666
  });
1494
- ctx.applyOp(op);
1667
+ await ctx.applyOp(op);
1495
1668
  return op;
1496
1669
  }
1497
1670
  async function setCriterionStatus(ctx, issueId, criterionIndex, status) {
@@ -1509,7 +1682,7 @@ async function setCriterionStatus(ctx, issueId, criterionIndex, status) {
1509
1682
  criterionStatus: status
1510
1683
  }
1511
1684
  });
1512
- ctx.applyOp(op);
1685
+ await ctx.applyOp(op);
1513
1686
  return op;
1514
1687
  }
1515
1688
  async function runCriteria(ctx, issueId, rootPath) {
@@ -1552,7 +1725,7 @@ async function runCriteria(ctx, issueId, rootPath) {
1552
1725
  criterionOutput: output.slice(0, 4096)
1553
1726
  }
1554
1727
  });
1555
- ctx.applyOp(updateOp);
1728
+ await ctx.applyOp(updateOp);
1556
1729
  results.push({
1557
1730
  id: c.id,
1558
1731
  description: c.description,
@@ -1625,4 +1798,262 @@ var init_issue = __esm(() => {
1625
1798
  execAsync = promisify(exec);
1626
1799
  });
1627
1800
 
1628
- export { decompose, init_decompose, BlobStore, init_blob_store, 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 };
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 };