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.
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-hy73j9z8.js → index-nq520y6k.js} +7 -3
  22. package/dist/{index-v9b4hqa7.js → index-nzb3am4f.js} +4 -1
  23. package/dist/{index-ncckgtrk.js → index-rv1p47gp.js} +1000 -114
  24. package/dist/{index-4gknc19b.js → index-w5wccer3.js} +1 -1
  25. package/dist/{index-a2a394zz.js → index-xhcp6xrn.js} +503 -41
  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-n71bmcy1.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
@@ -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) {
@@ -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 { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2 } from "fs";
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
- writeFileSync3(counterPath, JSON.stringify({ counter }, null, 2));
1176
- return `TRL-${counter}`;
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
- 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 };