@gitgov/core 2.6.0 → 2.7.0

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.
@@ -1,4 +1,5 @@
1
1
  import picomatch from 'picomatch';
2
+ import path from 'path';
2
3
 
3
4
  // src/file_lister/github/github_file_lister.ts
4
5
 
@@ -935,10 +936,10 @@ var GitHubGitModule = class {
935
936
  });
936
937
  const treeSha = commitData.tree.sha;
937
938
  const treeEntries = [];
938
- for (const [path, content] of this.stagingBuffer) {
939
+ for (const [path2, content] of this.stagingBuffer) {
939
940
  if (content === null) {
940
941
  treeEntries.push({
941
- path,
942
+ path: path2,
942
943
  mode: "100644",
943
944
  type: "blob",
944
945
  sha: null
@@ -951,7 +952,7 @@ var GitHubGitModule = class {
951
952
  encoding: "base64"
952
953
  });
953
954
  treeEntries.push({
954
- path,
955
+ path: path2,
955
956
  mode: "100644",
956
957
  type: "blob",
957
958
  sha: blobData.sha
@@ -1154,12 +1155,12 @@ var GitHubConfigStore = class {
1154
1155
  * [EARS-B2] Caches SHA from response for subsequent saveConfig.
1155
1156
  */
1156
1157
  async loadConfig() {
1157
- const path = `${this.basePath}/config.json`;
1158
+ const path2 = `${this.basePath}/config.json`;
1158
1159
  try {
1159
1160
  const { data } = await this.octokit.rest.repos.getContent({
1160
1161
  owner: this.owner,
1161
1162
  repo: this.repo,
1162
- path,
1163
+ path: path2,
1163
1164
  ref: this.ref
1164
1165
  });
1165
1166
  if (Array.isArray(data) || data.type !== "file") {
@@ -1179,7 +1180,7 @@ var GitHubConfigStore = class {
1179
1180
  if (isOctokitRequestError(error) && error.status === 404) {
1180
1181
  return null;
1181
1182
  }
1182
- throw mapOctokitError(error, `loadConfig ${this.owner}/${this.repo}/${path}`);
1183
+ throw mapOctokitError(error, `loadConfig ${this.owner}/${this.repo}/${path2}`);
1183
1184
  }
1184
1185
  }
1185
1186
  /**
@@ -1193,13 +1194,13 @@ var GitHubConfigStore = class {
1193
1194
  * [EARS-C3] Throws SERVER_ERROR on 5xx.
1194
1195
  */
1195
1196
  async saveConfig(config) {
1196
- const path = `${this.basePath}/config.json`;
1197
+ const path2 = `${this.basePath}/config.json`;
1197
1198
  const content = Buffer.from(JSON.stringify(config, null, 2)).toString("base64");
1198
1199
  try {
1199
1200
  const { data } = await this.octokit.rest.repos.createOrUpdateFileContents({
1200
1201
  owner: this.owner,
1201
1202
  repo: this.repo,
1202
- path,
1203
+ path: path2,
1203
1204
  message: "chore(config): update gitgov config.json",
1204
1205
  content,
1205
1206
  branch: this.ref,
@@ -1210,11 +1211,651 @@ var GitHubConfigStore = class {
1210
1211
  }
1211
1212
  return { commitSha: data.commit.sha };
1212
1213
  } catch (error) {
1213
- throw mapOctokitError(error, `saveConfig ${this.owner}/${this.repo}/${path}`);
1214
+ throw mapOctokitError(error, `saveConfig ${this.owner}/${this.repo}/${path2}`);
1214
1215
  }
1215
1216
  }
1216
1217
  };
1217
1218
 
1219
+ // src/sync_state/sync_state.types.ts
1220
+ var SYNC_DIRECTORIES = [
1221
+ "tasks",
1222
+ "cycles",
1223
+ "actors",
1224
+ "agents",
1225
+ "feedbacks",
1226
+ "executions",
1227
+ "changelogs",
1228
+ "workflows"
1229
+ ];
1230
+ var SYNC_ROOT_FILES = [
1231
+ "config.json"
1232
+ ];
1233
+ var SYNC_ALLOWED_EXTENSIONS = [".json"];
1234
+ var SYNC_EXCLUDED_PATTERNS = [
1235
+ /\.key$/,
1236
+ // Private keys (e.g., keys/*.key)
1237
+ /\.backup$/,
1238
+ // Backup files from lint
1239
+ /\.backup-\d+$/,
1240
+ // Numbered backup files
1241
+ /\.tmp$/,
1242
+ // Temporary files
1243
+ /\.bak$/
1244
+ // Backup files
1245
+ ];
1246
+ var LOCAL_ONLY_FILES = [
1247
+ "index.json",
1248
+ // Generated index, rebuilt on each machine
1249
+ ".session.json",
1250
+ // Local session state for current user/agent
1251
+ "gitgov"
1252
+ // Local binary/script
1253
+ ];
1254
+
1255
+ // src/sync_state/sync_state.utils.ts
1256
+ function shouldSyncFile(filePath) {
1257
+ const fileName = path.basename(filePath);
1258
+ const ext = path.extname(filePath);
1259
+ if (!SYNC_ALLOWED_EXTENSIONS.includes(ext)) {
1260
+ return false;
1261
+ }
1262
+ for (const pattern of SYNC_EXCLUDED_PATTERNS) {
1263
+ if (pattern.test(fileName)) {
1264
+ return false;
1265
+ }
1266
+ }
1267
+ if (LOCAL_ONLY_FILES.includes(fileName)) {
1268
+ return false;
1269
+ }
1270
+ const normalizedPath = filePath.replace(/\\/g, "/");
1271
+ const parts = normalizedPath.split("/");
1272
+ const gitgovIndex = parts.findIndex((p) => p === ".gitgov");
1273
+ let relativeParts;
1274
+ if (gitgovIndex !== -1) {
1275
+ relativeParts = parts.slice(gitgovIndex + 1);
1276
+ } else {
1277
+ const syncDirIndex = parts.findIndex(
1278
+ (p) => SYNC_DIRECTORIES.includes(p)
1279
+ );
1280
+ if (syncDirIndex !== -1) {
1281
+ relativeParts = parts.slice(syncDirIndex);
1282
+ } else if (SYNC_ROOT_FILES.includes(fileName)) {
1283
+ return true;
1284
+ } else {
1285
+ return false;
1286
+ }
1287
+ }
1288
+ if (relativeParts.length === 1) {
1289
+ return SYNC_ROOT_FILES.includes(relativeParts[0]);
1290
+ } else if (relativeParts.length >= 2) {
1291
+ const dirName = relativeParts[0];
1292
+ return SYNC_DIRECTORIES.includes(dirName);
1293
+ }
1294
+ return false;
1295
+ }
1296
+
1297
+ // src/sync_state/github_sync_state/github_sync_state.ts
1298
+ var GithubSyncStateModule = class {
1299
+ deps;
1300
+ lastKnownSha = null;
1301
+ constructor(deps) {
1302
+ this.deps = deps;
1303
+ }
1304
+ // ==================== Block A: Branch Management ====================
1305
+ /**
1306
+ * [EARS-GS-A3] Returns the configured state branch name.
1307
+ */
1308
+ async getStateBranchName() {
1309
+ return "gitgov-state";
1310
+ }
1311
+ /**
1312
+ * [EARS-GS-A1] Creates gitgov-state branch if it does not exist.
1313
+ * [EARS-GS-A2] Idempotent — no-op if branch already exists.
1314
+ */
1315
+ async ensureStateBranch() {
1316
+ const branchName = await this.getStateBranchName();
1317
+ try {
1318
+ await this.deps.octokit.rest.repos.getBranch({
1319
+ owner: this.deps.owner,
1320
+ repo: this.deps.repo,
1321
+ branch: branchName
1322
+ });
1323
+ return;
1324
+ } catch (error) {
1325
+ if (!isOctokitRequestError(error) || error.status !== 404) {
1326
+ throw error;
1327
+ }
1328
+ }
1329
+ const { data: repoData } = await this.deps.octokit.rest.repos.get({
1330
+ owner: this.deps.owner,
1331
+ repo: this.deps.repo
1332
+ });
1333
+ const defaultBranch = repoData.default_branch;
1334
+ const { data: refData } = await this.deps.octokit.rest.git.getRef({
1335
+ owner: this.deps.owner,
1336
+ repo: this.deps.repo,
1337
+ ref: `heads/${defaultBranch}`
1338
+ });
1339
+ await this.deps.octokit.rest.git.createRef({
1340
+ owner: this.deps.owner,
1341
+ repo: this.deps.repo,
1342
+ ref: `refs/heads/${branchName}`,
1343
+ sha: refData.object.sha
1344
+ });
1345
+ }
1346
+ // ==================== Block B: Push State ====================
1347
+ /**
1348
+ * [EARS-GS-B1..B5] Push local .gitgov/ state to gitgov-state branch via API.
1349
+ *
1350
+ * Uses the 6-step atomic commit pattern:
1351
+ * getRef → getCommit → createBlob → createTree → createCommit → updateRef
1352
+ *
1353
+ * Optimistic concurrency: if remote ref advanced since our read, updateRef
1354
+ * fails with 422 → return conflictDetected: true.
1355
+ */
1356
+ async pushState(options) {
1357
+ const branchName = await this.getStateBranchName();
1358
+ const sourceBranch = options.sourceBranch ?? "main";
1359
+ try {
1360
+ const { data: stateRefData } = await this.deps.octokit.rest.git.getRef({
1361
+ owner: this.deps.owner,
1362
+ repo: this.deps.repo,
1363
+ ref: `heads/${branchName}`
1364
+ });
1365
+ const currentSha = stateRefData.object.sha;
1366
+ const { data: sourceTree } = await this.deps.octokit.rest.git.getTree({
1367
+ owner: this.deps.owner,
1368
+ repo: this.deps.repo,
1369
+ tree_sha: sourceBranch,
1370
+ recursive: "true"
1371
+ });
1372
+ const sourceFiles = (sourceTree.tree ?? []).filter(
1373
+ (item) => item.type === "blob" && item.path?.startsWith(".gitgov/") && shouldSyncFile(item.path)
1374
+ );
1375
+ const { data: targetCommit } = await this.deps.octokit.rest.git.getCommit({
1376
+ owner: this.deps.owner,
1377
+ repo: this.deps.repo,
1378
+ commit_sha: currentSha
1379
+ });
1380
+ const { data: targetTree } = await this.deps.octokit.rest.git.getTree({
1381
+ owner: this.deps.owner,
1382
+ repo: this.deps.repo,
1383
+ tree_sha: targetCommit.tree.sha,
1384
+ recursive: "true"
1385
+ });
1386
+ const targetFileMap = /* @__PURE__ */ new Map();
1387
+ for (const item of targetTree.tree ?? []) {
1388
+ if (item.type === "blob" && item.path && item.sha) {
1389
+ targetFileMap.set(item.path, item.sha);
1390
+ }
1391
+ }
1392
+ const delta = [];
1393
+ const treeEntries = [];
1394
+ for (const sourceFile of sourceFiles) {
1395
+ if (!sourceFile.path || !sourceFile.sha) continue;
1396
+ const statePath = sourceFile.path.replace(/^\.gitgov\//, "");
1397
+ const targetSha = targetFileMap.get(statePath);
1398
+ if (targetSha !== sourceFile.sha) {
1399
+ delta.push({
1400
+ status: targetSha ? "M" : "A",
1401
+ file: statePath
1402
+ });
1403
+ treeEntries.push({
1404
+ path: statePath,
1405
+ mode: "100644",
1406
+ type: "blob",
1407
+ sha: sourceFile.sha
1408
+ });
1409
+ }
1410
+ targetFileMap.delete(statePath);
1411
+ }
1412
+ for (const [deletedPath] of targetFileMap) {
1413
+ if (shouldSyncFile(deletedPath)) {
1414
+ delta.push({ status: "D", file: deletedPath });
1415
+ treeEntries.push({
1416
+ path: deletedPath,
1417
+ mode: "100644",
1418
+ type: "blob",
1419
+ sha: null
1420
+ });
1421
+ }
1422
+ }
1423
+ if (delta.length === 0) {
1424
+ return {
1425
+ success: true,
1426
+ filesSynced: 0,
1427
+ sourceBranch,
1428
+ commitHash: null,
1429
+ commitMessage: null,
1430
+ conflictDetected: false
1431
+ };
1432
+ }
1433
+ if (options.dryRun) {
1434
+ return {
1435
+ success: true,
1436
+ filesSynced: delta.length,
1437
+ sourceBranch,
1438
+ commitHash: null,
1439
+ commitMessage: `[dry-run] gitgov sync: ${delta.length} files`,
1440
+ conflictDetected: false
1441
+ };
1442
+ }
1443
+ const { data: newTreeData } = await this.deps.octokit.rest.git.createTree({
1444
+ owner: this.deps.owner,
1445
+ repo: this.deps.repo,
1446
+ base_tree: targetCommit.tree.sha,
1447
+ tree: treeEntries
1448
+ });
1449
+ const commitMessage = `gitgov sync: ${delta.length} files from ${sourceBranch}`;
1450
+ const { data: newCommitData } = await this.deps.octokit.rest.git.createCommit({
1451
+ owner: this.deps.owner,
1452
+ repo: this.deps.repo,
1453
+ message: commitMessage,
1454
+ tree: newTreeData.sha,
1455
+ parents: [currentSha]
1456
+ });
1457
+ try {
1458
+ await this.deps.octokit.rest.git.updateRef({
1459
+ owner: this.deps.owner,
1460
+ repo: this.deps.repo,
1461
+ ref: `heads/${branchName}`,
1462
+ sha: newCommitData.sha
1463
+ });
1464
+ } catch (error) {
1465
+ if (isOctokitRequestError(error) && (error.status === 422 || error.status === 409)) {
1466
+ return {
1467
+ success: false,
1468
+ filesSynced: 0,
1469
+ sourceBranch,
1470
+ commitHash: null,
1471
+ commitMessage: null,
1472
+ conflictDetected: true,
1473
+ conflictInfo: {
1474
+ type: "rebase_conflict",
1475
+ affectedFiles: delta.map((d) => d.file),
1476
+ message: "Remote gitgov-state ref has advanced since last read. Pull and retry.",
1477
+ resolutionSteps: [
1478
+ "Call pullState() to fetch latest remote state",
1479
+ "Retry pushState() with updated parent SHA"
1480
+ ]
1481
+ }
1482
+ };
1483
+ }
1484
+ throw error;
1485
+ }
1486
+ this.lastKnownSha = newCommitData.sha;
1487
+ return {
1488
+ success: true,
1489
+ filesSynced: delta.length,
1490
+ sourceBranch,
1491
+ commitHash: newCommitData.sha,
1492
+ commitMessage,
1493
+ conflictDetected: false
1494
+ };
1495
+ } catch (error) {
1496
+ if (isOctokitRequestError(error)) {
1497
+ return {
1498
+ success: false,
1499
+ filesSynced: 0,
1500
+ sourceBranch,
1501
+ commitHash: null,
1502
+ commitMessage: null,
1503
+ conflictDetected: false,
1504
+ error: `GitHub API error (${error.status}): ${error.message}`
1505
+ };
1506
+ }
1507
+ const msg = error instanceof Error ? error.message : String(error);
1508
+ return {
1509
+ success: false,
1510
+ filesSynced: 0,
1511
+ sourceBranch,
1512
+ commitHash: null,
1513
+ commitMessage: null,
1514
+ conflictDetected: false,
1515
+ error: msg
1516
+ };
1517
+ }
1518
+ }
1519
+ // ==================== Block C: Pull State ====================
1520
+ /**
1521
+ * [EARS-GS-C1..C4] Pull remote state from gitgov-state branch.
1522
+ *
1523
+ * Fetches tree + blobs, updates lastKnownSha, triggers re-indexing.
1524
+ */
1525
+ async pullState(options) {
1526
+ const branchName = await this.getStateBranchName();
1527
+ let remoteSha;
1528
+ try {
1529
+ const { data: refData } = await this.deps.octokit.rest.git.getRef({
1530
+ owner: this.deps.owner,
1531
+ repo: this.deps.repo,
1532
+ ref: `heads/${branchName}`
1533
+ });
1534
+ remoteSha = refData.object.sha;
1535
+ } catch (error) {
1536
+ if (isOctokitRequestError(error) && error.status === 404) {
1537
+ return {
1538
+ success: true,
1539
+ hasChanges: false,
1540
+ filesUpdated: 0,
1541
+ reindexed: false,
1542
+ conflictDetected: false
1543
+ };
1544
+ }
1545
+ throw error;
1546
+ }
1547
+ if (this.lastKnownSha === remoteSha && !options?.forceReindex) {
1548
+ return {
1549
+ success: true,
1550
+ hasChanges: false,
1551
+ filesUpdated: 0,
1552
+ reindexed: false,
1553
+ conflictDetected: false
1554
+ };
1555
+ }
1556
+ const { data: commitData } = await this.deps.octokit.rest.git.getCommit({
1557
+ owner: this.deps.owner,
1558
+ repo: this.deps.repo,
1559
+ commit_sha: remoteSha
1560
+ });
1561
+ const { data: treeData } = await this.deps.octokit.rest.git.getTree({
1562
+ owner: this.deps.owner,
1563
+ repo: this.deps.repo,
1564
+ tree_sha: commitData.tree.sha,
1565
+ recursive: "true"
1566
+ });
1567
+ const syncableFiles = (treeData.tree ?? []).filter(
1568
+ (item) => item.type === "blob" && item.path && shouldSyncFile(item.path)
1569
+ );
1570
+ const filesUpdated = syncableFiles.length;
1571
+ this.lastKnownSha = remoteSha;
1572
+ let reindexed = false;
1573
+ if (filesUpdated > 0 || options?.forceReindex) {
1574
+ try {
1575
+ await this.deps.indexer.computeProjection();
1576
+ reindexed = true;
1577
+ } catch {
1578
+ reindexed = false;
1579
+ }
1580
+ }
1581
+ return {
1582
+ success: true,
1583
+ hasChanges: filesUpdated > 0,
1584
+ filesUpdated,
1585
+ reindexed,
1586
+ conflictDetected: false
1587
+ };
1588
+ }
1589
+ // ==================== Block D: Change Detection ====================
1590
+ /**
1591
+ * [EARS-GS-D1..D3] Calculate file delta between known state and current remote.
1592
+ */
1593
+ async calculateStateDelta(_sourceBranch) {
1594
+ const branchName = await this.getStateBranchName();
1595
+ let currentSha;
1596
+ try {
1597
+ const { data: refData } = await this.deps.octokit.rest.git.getRef({
1598
+ owner: this.deps.owner,
1599
+ repo: this.deps.repo,
1600
+ ref: `heads/${branchName}`
1601
+ });
1602
+ currentSha = refData.object.sha;
1603
+ } catch (error) {
1604
+ if (isOctokitRequestError(error) && error.status === 404) {
1605
+ return [];
1606
+ }
1607
+ throw error;
1608
+ }
1609
+ if (this.lastKnownSha === currentSha) {
1610
+ return [];
1611
+ }
1612
+ if (this.lastKnownSha === null) {
1613
+ const { data: commitData } = await this.deps.octokit.rest.git.getCommit({
1614
+ owner: this.deps.owner,
1615
+ repo: this.deps.repo,
1616
+ commit_sha: currentSha
1617
+ });
1618
+ const { data: treeData } = await this.deps.octokit.rest.git.getTree({
1619
+ owner: this.deps.owner,
1620
+ repo: this.deps.repo,
1621
+ tree_sha: commitData.tree.sha,
1622
+ recursive: "true"
1623
+ });
1624
+ return (treeData.tree ?? []).filter((item) => item.type === "blob" && item.path && shouldSyncFile(item.path)).map((item) => ({
1625
+ status: "A",
1626
+ file: item.path
1627
+ }));
1628
+ }
1629
+ const { data: comparison } = await this.deps.octokit.rest.repos.compareCommits({
1630
+ owner: this.deps.owner,
1631
+ repo: this.deps.repo,
1632
+ base: this.lastKnownSha,
1633
+ head: currentSha
1634
+ });
1635
+ return (comparison.files ?? []).filter((file) => shouldSyncFile(file.filename)).map((file) => ({
1636
+ status: file.status === "added" ? "A" : file.status === "removed" ? "D" : "M",
1637
+ file: file.filename
1638
+ }));
1639
+ }
1640
+ /**
1641
+ * Always empty — no local pending changes in API mode.
1642
+ * In API mode there is no local filesystem; all state is remote.
1643
+ */
1644
+ async getPendingChanges() {
1645
+ return [];
1646
+ }
1647
+ // ==================== Block E: Conflict Handling ====================
1648
+ /**
1649
+ * Always false — no rebase in API mode.
1650
+ */
1651
+ async isRebaseInProgress() {
1652
+ return false;
1653
+ }
1654
+ /**
1655
+ * Always empty — no conflict markers in API mode.
1656
+ */
1657
+ async checkConflictMarkers(_filePaths) {
1658
+ return [];
1659
+ }
1660
+ /**
1661
+ * Empty diff — no git-level conflict markers in API mode.
1662
+ */
1663
+ async getConflictDiff(_filePaths) {
1664
+ return {
1665
+ files: [],
1666
+ message: "No conflict markers in API mode. Conflicts are SHA-based.",
1667
+ resolutionSteps: [
1668
+ "Call pullState() to fetch latest remote state",
1669
+ "Retry pushState() with updated records"
1670
+ ]
1671
+ };
1672
+ }
1673
+ /**
1674
+ * [EARS-GS-E1..E2] Resolve conflict by pulling latest and retrying push.
1675
+ */
1676
+ async resolveConflict(options) {
1677
+ const pullResult = await this.pullState({ forceReindex: false });
1678
+ if (!pullResult.success) {
1679
+ return {
1680
+ success: false,
1681
+ rebaseCommitHash: "",
1682
+ resolutionCommitHash: "",
1683
+ conflictsResolved: 0,
1684
+ resolvedBy: options.actorId,
1685
+ reason: options.reason,
1686
+ error: `Pull failed during conflict resolution: ${pullResult.error}`
1687
+ };
1688
+ }
1689
+ const pushResult = await this.pushState({
1690
+ actorId: options.actorId
1691
+ });
1692
+ if (!pushResult.success || pushResult.conflictDetected) {
1693
+ const errorMsg = pushResult.conflictDetected ? "Content conflict: same file modified by both sides. Manual resolution required." : pushResult.error ?? "Unknown push error";
1694
+ return {
1695
+ success: false,
1696
+ rebaseCommitHash: "",
1697
+ resolutionCommitHash: "",
1698
+ conflictsResolved: 0,
1699
+ resolvedBy: options.actorId,
1700
+ reason: options.reason,
1701
+ error: errorMsg
1702
+ };
1703
+ }
1704
+ return {
1705
+ success: true,
1706
+ rebaseCommitHash: this.lastKnownSha ?? "",
1707
+ resolutionCommitHash: pushResult.commitHash ?? "",
1708
+ conflictsResolved: pushResult.filesSynced,
1709
+ resolvedBy: options.actorId,
1710
+ reason: options.reason
1711
+ };
1712
+ }
1713
+ /**
1714
+ * No integrity violations in API mode (no rebase commits).
1715
+ */
1716
+ async verifyResolutionIntegrity() {
1717
+ return [];
1718
+ }
1719
+ // ==================== Block F: Audit ====================
1720
+ /**
1721
+ * [EARS-GS-F1..F2] Audit the remote gitgov-state branch.
1722
+ */
1723
+ async auditState(options) {
1724
+ const branchName = await this.getStateBranchName();
1725
+ const scope = options?.scope ?? "all";
1726
+ let totalCommits = 0;
1727
+ try {
1728
+ const { data: commits } = await this.deps.octokit.rest.repos.listCommits({
1729
+ owner: this.deps.owner,
1730
+ repo: this.deps.repo,
1731
+ sha: branchName,
1732
+ per_page: 100
1733
+ });
1734
+ totalCommits = commits.length;
1735
+ } catch (error) {
1736
+ if (isOctokitRequestError(error) && error.status === 404) {
1737
+ return {
1738
+ passed: true,
1739
+ scope,
1740
+ totalCommits: 0,
1741
+ rebaseCommits: 0,
1742
+ resolutionCommits: 0,
1743
+ integrityViolations: [],
1744
+ summary: "Branch gitgov-state does not exist. No audit needed."
1745
+ };
1746
+ }
1747
+ throw error;
1748
+ }
1749
+ const rebaseCommits = 0;
1750
+ const resolutionCommits = 0;
1751
+ const integrityViolations = [];
1752
+ let lintReport;
1753
+ if (options?.verifySignatures !== false || options?.verifyChecksums !== false) {
1754
+ try {
1755
+ const { data: refData } = await this.deps.octokit.rest.git.getRef({
1756
+ owner: this.deps.owner,
1757
+ repo: this.deps.repo,
1758
+ ref: `heads/${branchName}`
1759
+ });
1760
+ const { data: commitData } = await this.deps.octokit.rest.git.getCommit({
1761
+ owner: this.deps.owner,
1762
+ repo: this.deps.repo,
1763
+ commit_sha: refData.object.sha
1764
+ });
1765
+ const { data: treeData } = await this.deps.octokit.rest.git.getTree({
1766
+ owner: this.deps.owner,
1767
+ repo: this.deps.repo,
1768
+ tree_sha: commitData.tree.sha,
1769
+ recursive: "true"
1770
+ });
1771
+ const treeItems = (treeData.tree ?? []).filter((item) => item.type === "blob" && item.path && item.sha && shouldSyncFile(item.path));
1772
+ const startTime = Date.now();
1773
+ const allResults = [];
1774
+ let filesChecked = 0;
1775
+ for (const item of treeItems) {
1776
+ try {
1777
+ const { data: blobData } = await this.deps.octokit.rest.git.getBlob({
1778
+ owner: this.deps.owner,
1779
+ repo: this.deps.repo,
1780
+ file_sha: item.sha
1781
+ });
1782
+ const content = Buffer.from(blobData.content, "base64").toString("utf-8");
1783
+ const record = JSON.parse(content);
1784
+ const entityType = pathToEntityType(item.path);
1785
+ if (entityType) {
1786
+ const results = this.deps.lint.lintRecord(record, {
1787
+ recordId: item.path.split("/").pop()?.replace(".json", "") ?? item.path,
1788
+ entityType,
1789
+ filePath: item.path
1790
+ });
1791
+ allResults.push(...results);
1792
+ }
1793
+ filesChecked++;
1794
+ } catch {
1795
+ }
1796
+ }
1797
+ if (filesChecked > 0) {
1798
+ lintReport = {
1799
+ summary: {
1800
+ filesChecked,
1801
+ errors: allResults.filter((r) => r.level === "error").length,
1802
+ warnings: allResults.filter((r) => r.level === "warning").length,
1803
+ fixable: allResults.filter((r) => r.fixable).length,
1804
+ executionTime: Date.now() - startTime
1805
+ },
1806
+ results: allResults,
1807
+ metadata: {
1808
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1809
+ options: {},
1810
+ version: "1.0.0"
1811
+ }
1812
+ };
1813
+ }
1814
+ } catch {
1815
+ }
1816
+ }
1817
+ const lintPassed = !lintReport || lintReport.summary.errors === 0;
1818
+ const passed = integrityViolations.length === 0 && lintPassed;
1819
+ const lintErrors = lintReport?.summary.errors ?? 0;
1820
+ let summary;
1821
+ if (passed) {
1822
+ summary = `Audit passed. ${totalCommits} commits analyzed, 0 violations.`;
1823
+ } else if (integrityViolations.length > 0 && lintErrors > 0) {
1824
+ summary = `Audit failed. ${integrityViolations.length} integrity violations, ${lintErrors} lint errors.`;
1825
+ } else if (lintErrors > 0) {
1826
+ summary = `Audit failed. ${lintErrors} lint errors found.`;
1827
+ } else {
1828
+ summary = `Audit failed. ${integrityViolations.length} integrity violations found.`;
1829
+ }
1830
+ const report = {
1831
+ passed,
1832
+ scope,
1833
+ totalCommits,
1834
+ rebaseCommits,
1835
+ resolutionCommits,
1836
+ integrityViolations,
1837
+ summary
1838
+ };
1839
+ if (lintReport) {
1840
+ report.lintReport = lintReport;
1841
+ }
1842
+ return report;
1843
+ }
1844
+ };
1845
+ function pathToEntityType(filePath) {
1846
+ const dirMap = {
1847
+ tasks: "task",
1848
+ cycles: "cycle",
1849
+ actors: "actor",
1850
+ agents: "agent",
1851
+ feedbacks: "feedback",
1852
+ executions: "execution",
1853
+ changelogs: "changelog"
1854
+ };
1855
+ const firstSegment = filePath.split("/")[0] ?? "";
1856
+ return dirMap[firstSegment];
1857
+ }
1858
+
1218
1859
  // src/github.ts
1219
1860
  var GitHubApiError = class _GitHubApiError extends Error {
1220
1861
  constructor(message, code, statusCode) {
@@ -1276,6 +1917,6 @@ function mapOctokitError(error, context) {
1276
1917
  return new GitHubApiError(`Network error: ${message}`, "NETWORK_ERROR");
1277
1918
  }
1278
1919
 
1279
- export { GitHubApiError, GitHubConfigStore, GitHubFileLister, GitHubGitModule, GitHubRecordStore, isOctokitRequestError, mapOctokitError };
1920
+ export { GitHubApiError, GitHubConfigStore, GitHubFileLister, GitHubGitModule, GitHubRecordStore, GithubSyncStateModule, isOctokitRequestError, mapOctokitError };
1280
1921
  //# sourceMappingURL=github.js.map
1281
1922
  //# sourceMappingURL=github.js.map