gitxelectron 1.3.9 → 1.4.1

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/out/main/index.js CHANGED
@@ -111,8 +111,24 @@ async function logout(sessionId) {
111
111
  }
112
112
  async function getAuthStatus(sessionId) {
113
113
  const activeAccount = getActiveAccount(sessionId);
114
- if (!activeAccount) return { status: "Not Authenticated", username: null };
115
- return { status: "Verified", username: activeAccount.username };
114
+ if (!activeAccount) {
115
+ return {
116
+ status: "Not Authenticated",
117
+ username: null,
118
+ avatarUrl: null,
119
+ authMethod: "Local Git Credentials",
120
+ aiProvider: "GitHub Models",
121
+ securityStatus: "Encrypted Local Keystore"
122
+ };
123
+ }
124
+ return {
125
+ status: "Verified",
126
+ username: activeAccount.username,
127
+ avatarUrl: activeAccount.avatar_url || null,
128
+ authMethod: "GitHub OAuth (Verified)",
129
+ aiProvider: "GitHub Models (gpt-4o-mini)",
130
+ securityStatus: "End-to-End Encrypted Session"
131
+ };
116
132
  }
117
133
  async function startDeviceFlow(_sessionId) {
118
134
  const response = await fetch("https://github.com/login/device/code", {
@@ -601,7 +617,7 @@ function initGit(sessionId, baseDir) {
601
617
  try {
602
618
  const wc = electron.webContents.fromId(sessionId);
603
619
  if (wc) wc.send("git-progress", { method, stage, progress });
604
- } catch (e) {
620
+ } catch {
605
621
  }
606
622
  }
607
623
  };
@@ -642,7 +658,7 @@ function createSafeGit(baseDir, sessionId) {
642
658
  try {
643
659
  const wc = electron.webContents.fromId(sessionId);
644
660
  if (wc) wc.send("git-progress", { method, stage, progress });
645
- } catch (e) {
661
+ } catch {
646
662
  }
647
663
  }
648
664
  };
@@ -701,9 +717,9 @@ async function setupDefaultGitAttributes(repoPath) {
701
717
  let existing = "";
702
718
  try {
703
719
  existing = await fs__namespace.readFile(attrsPath, "utf8");
704
- } catch (e) {
720
+ } catch {
705
721
  }
706
- let newLines = [];
722
+ const newLines = [];
707
723
  for (const ext of DEFAULT_LFS_EXTENSIONS) {
708
724
  if (!existing.includes(ext)) {
709
725
  newLines.push(`${ext} filter=lfs diff=lfs merge=lfs -text`);
@@ -713,7 +729,7 @@ async function setupDefaultGitAttributes(repoPath) {
713
729
  const appendContent = (existing && !existing.endsWith("\n") ? "\n" : "") + newLines.join("\n") + "\n";
714
730
  await fs__namespace.appendFile(attrsPath, appendContent);
715
731
  }
716
- } catch (e) {
732
+ } catch {
717
733
  }
718
734
  }
719
735
  async function isGitRepo(dir) {
@@ -843,6 +859,7 @@ async function getStatus(sessionId) {
843
859
  async function stageFiles(sessionId, files) {
844
860
  const { git, repoPath } = getGitSession(sessionId);
845
861
  const fileArray = Array.isArray(files) ? files : [files];
862
+ await ensureAiWeightsUseLfs(git);
846
863
  if (repoPath) {
847
864
  const LARGE_FILE_LIMIT = 50 * 1024 * 1024;
848
865
  const largeFiles = [];
@@ -853,7 +870,7 @@ async function stageFiles(sessionId, files) {
853
870
  if (stats.isFile() && stats.size > LARGE_FILE_LIMIT) {
854
871
  largeFiles.push(file);
855
872
  }
856
- } catch (e) {
873
+ } catch {
857
874
  }
858
875
  }
859
876
  if (largeFiles.length > 0) {
@@ -907,6 +924,7 @@ async function unstageFiles(sessionId, files) {
907
924
  }
908
925
  async function commitChanges(sessionId, message) {
909
926
  const { git } = getGitSession(sessionId);
927
+ await ensureAiWeightsUseLfs(git);
910
928
  try {
911
929
  const nameConfig = await git.getConfig("user.name");
912
930
  const emailConfig = await git.getConfig("user.email");
@@ -936,7 +954,7 @@ function injectAuthToken(sessionId, remoteUrl) {
936
954
  parsed.password = token;
937
955
  return parsed.toString();
938
956
  }
939
- } catch (e) {
957
+ } catch {
940
958
  if (remoteUrl.includes("github.com")) {
941
959
  return remoteUrl.replace(
942
960
  /https:\/\/(?:[^@]+@)?github\.com\//,
@@ -947,7 +965,8 @@ function injectAuthToken(sessionId, remoteUrl) {
947
965
  return remoteUrl;
948
966
  }
949
967
  async function pushMassiveRepository(sessionId, remote, branch, chunkSize = 5e3, onProgress, onDetailedProgress) {
950
- const { repoPath } = getGitSession(sessionId);
968
+ const { git, repoPath } = getGitSession(sessionId);
969
+ await ensureAiWeightsUseLfs(git);
951
970
  if (onProgress) onProgress("Starting rapid file count to prepare massive chunking...");
952
971
  const totalFiles = await new Promise((resolve) => {
953
972
  const child2 = child_process.spawn("git", ["status", "--porcelain"], { cwd: repoPath });
@@ -965,7 +984,6 @@ async function pushMassiveRepository(sessionId, remote, branch, chunkSize = 5e3,
965
984
  return true;
966
985
  }
967
986
  if (onProgress) onProgress(`Total files to push: ${totalFiles}. Starting chunked upload...`);
968
- const { git } = getGitSession(sessionId);
969
987
  const remotes = await getRemotes(sessionId);
970
988
  const remoteObj = remotes.find((r) => r.name === remote);
971
989
  if (onProgress) onProgress("Starting massive repository push...");
@@ -1018,7 +1036,7 @@ async function pushMassiveRepository(sessionId, remote, branch, chunkSize = 5e3,
1018
1036
  if (stats.isFile() && stats.size > LARGE_FILE_LIMIT) {
1019
1037
  largeFiles.push(file);
1020
1038
  }
1021
- } catch (e) {
1039
+ } catch {
1022
1040
  }
1023
1041
  })
1024
1042
  );
@@ -1055,7 +1073,7 @@ async function pushMassiveRepository(sessionId, remote, branch, chunkSize = 5e3,
1055
1073
  } finally {
1056
1074
  try {
1057
1075
  await fs__namespace.unlink(tempFile);
1058
- } catch (e) {
1076
+ } catch {
1059
1077
  }
1060
1078
  }
1061
1079
  };
@@ -1083,7 +1101,7 @@ async function pushMassiveRepository(sessionId, remote, branch, chunkSize = 5e3,
1083
1101
  if (onProgress) onProgress("Committing all files...");
1084
1102
  try {
1085
1103
  await git.commit(`Auto-sync (Massive Push)`);
1086
- } catch (e) {
1104
+ } catch {
1087
1105
  }
1088
1106
  if (onProgress) onProgress("Pushing to remote...");
1089
1107
  const pushArgs = [
@@ -1108,7 +1126,7 @@ async function pushMassiveRepository(sessionId, remote, branch, chunkSize = 5e3,
1108
1126
  } catch (err) {
1109
1127
  if (err.message && (err.message.includes("non-fast-forward") || err.message.includes("fetch first") || err.message.includes("branch is behind"))) {
1110
1128
  if (onProgress) onProgress("Remote branch is ahead. Pulling changes...");
1111
- await git.pull(authUrl, branch, { "--rebase": null, "--autostash": null });
1129
+ await git.pull(authUrl, branch, { "--rebase": "false", "--autostash": null, "-X": "ours" });
1112
1130
  if (onProgress) onProgress("Pushing to remote after pull...");
1113
1131
  await git.raw([...pushArgs, authUrl, branch]);
1114
1132
  } else {
@@ -1162,7 +1180,7 @@ async function pushToRemote(sessionId, remote, branch) {
1162
1180
  if (err.message && (err.message.includes("non-fast-forward") || err.message.includes("fetch first") || err.message.includes("branch is behind"))) {
1163
1181
  console.log("Push rejected because branch is behind. Auto-pulling with rebase...");
1164
1182
  const { git } = getGitSession(sessionId);
1165
- const options = { "--rebase": null, "--autostash": null };
1183
+ const options = { "--rebase": "false", "--autostash": null, "-X": "ours" };
1166
1184
  if (remoteObj && remoteObj.refs.fetch) {
1167
1185
  const authUrl = injectAuthToken(sessionId, remoteObj.refs.fetch);
1168
1186
  await git.pull(authUrl, branch, options);
@@ -1175,10 +1193,40 @@ async function pushToRemote(sessionId, remote, branch) {
1175
1193
  }
1176
1194
  }
1177
1195
  async function masterForcePush(sessionId, remote, branch) {
1178
- const { git } = getGitSession(sessionId);
1196
+ const { git, repoPath } = getGitSession(sessionId);
1179
1197
  const remotes = await getRemotes(sessionId);
1180
- const remoteObj = remotes.find((r) => r.name === remote);
1181
- await git.raw(["add", "-A", "-f"]);
1198
+ const remoteObj = (remote ? remotes.find((r) => r.name === remote) : null) || remotes[0];
1199
+ try {
1200
+ const branchSummary = await git.branchLocal();
1201
+ let current = branchSummary.current || branch;
1202
+ if (current && current.startsWith("__master_push_temp_")) {
1203
+ const candidates = ["main", "master", branch].filter(
1204
+ (b) => b && !b.startsWith("__master_push_temp_") && branchSummary.all.includes(b)
1205
+ );
1206
+ const fallbackBranch = candidates[0] || "main";
1207
+ try {
1208
+ await git.checkout(fallbackBranch);
1209
+ current = fallbackBranch;
1210
+ branch = fallbackBranch;
1211
+ } catch {
1212
+ }
1213
+ }
1214
+ for (const b of branchSummary.all) {
1215
+ if (b.startsWith("__master_push_temp_") && b !== current) {
1216
+ try {
1217
+ await git.deleteLocalBranch(b, true);
1218
+ } catch {
1219
+ }
1220
+ }
1221
+ }
1222
+ } catch {
1223
+ }
1224
+ await ensureAiWeightsUseLfs(git);
1225
+ try {
1226
+ await git.add(".");
1227
+ } catch {
1228
+ await git.raw(["add", "-A"]);
1229
+ }
1182
1230
  try {
1183
1231
  const name = await git.getConfig("user.name");
1184
1232
  if (!name.value) await git.addConfig("user.name", "GitElectron User");
@@ -1188,34 +1236,40 @@ async function masterForcePush(sessionId, remote, branch) {
1188
1236
  console.warn("Failed to ensure git identity before master push:", e);
1189
1237
  }
1190
1238
  try {
1191
- await git.commit("Master Force Push - Including all files");
1239
+ await git.commit("Master Force Push - Sync changes");
1192
1240
  } catch (e) {
1193
1241
  if (!e.message.includes("nothing to commit")) {
1194
- throw e;
1242
+ console.warn("Commit note:", e.message);
1195
1243
  }
1196
1244
  }
1197
1245
  const tempGit = simpleGit.simpleGit({
1198
- baseDir: getGitSession(sessionId).repoPath,
1246
+ baseDir: repoPath,
1199
1247
  unsafe: { allowUnsafeEditor: true, allowUnsafeAskPass: true, allowUnsafeCredentialHelper: true }
1200
1248
  }).env({
1201
1249
  ...process.env,
1202
1250
  GCM_INTERACTIVE: "false",
1203
1251
  GIT_TERMINAL_PROMPT: "0",
1204
- GIT_ASKPASS: "echo"
1252
+ GIT_ASKPASS: "echo",
1253
+ GIT_LFS_SKIP_SMUDGE: "1",
1254
+ GIT_LFS_SKIP_PUSH: "1"
1205
1255
  });
1206
1256
  const pushArgs = [
1207
1257
  "-c",
1208
1258
  "credential.helper=",
1259
+ "-c",
1260
+ "filter.lfs.required=false",
1261
+ "-c",
1262
+ "lfs.allowincompletepush=true",
1209
1263
  "push",
1210
1264
  "--progress",
1211
1265
  "--no-verify",
1212
1266
  "-f"
1213
1267
  ];
1214
- if (remoteObj && remoteObj.refs.push) {
1268
+ if (remoteObj && remoteObj.refs?.push) {
1215
1269
  const authUrl = injectAuthToken(sessionId, remoteObj.refs.push);
1216
1270
  return await tempGit.raw([...pushArgs, authUrl, branch]);
1217
1271
  }
1218
- return await tempGit.raw([...pushArgs, remote, branch]);
1272
+ return await tempGit.raw([...pushArgs, remote || "origin", branch]);
1219
1273
  }
1220
1274
  async function pullFromRemote(sessionId, remote, branch) {
1221
1275
  const { git, repoPath } = getGitSession(sessionId);
@@ -1280,6 +1334,8 @@ async function pullFromRemote(sessionId, remote, branch) {
1280
1334
  "pull",
1281
1335
  "--no-rebase",
1282
1336
  "--allow-unrelated-histories",
1337
+ "-X",
1338
+ "ours",
1283
1339
  authUrl,
1284
1340
  targetBranch
1285
1341
  ];
@@ -1439,7 +1495,7 @@ async function getBranches(sessionId) {
1439
1495
  )
1440
1496
  ]);
1441
1497
  }
1442
- } catch (e) {
1498
+ } catch {
1443
1499
  }
1444
1500
  let branches = await git.branch(["-a"]);
1445
1501
  if (branches.all.length === 0) {
@@ -1461,10 +1517,14 @@ async function getBranches(sessionId) {
1461
1517
  }
1462
1518
  const allBranches = /* @__PURE__ */ new Set();
1463
1519
  branches.all.forEach((b) => {
1520
+ if (b.includes("__master_push_temp_")) return;
1464
1521
  if (b.startsWith("remotes/")) {
1465
1522
  const parts = b.split("/");
1466
1523
  if (parts.length > 2) {
1467
- allBranches.add(parts.slice(2).join("/"));
1524
+ const branchName = parts.slice(2).join("/");
1525
+ if (!branchName.includes("__master_push_temp_")) {
1526
+ allBranches.add(branchName);
1527
+ }
1468
1528
  }
1469
1529
  } else {
1470
1530
  allBranches.add(b);
@@ -1551,15 +1611,11 @@ async function pushAllBranches(sessionId, remote) {
1551
1611
  GIT_TERMINAL_PROMPT: "0",
1552
1612
  GIT_ASKPASS: "echo"
1553
1613
  });
1554
- try {
1555
- if (remoteObj && remoteObj.refs.push) {
1556
- const authUrl = injectAuthToken(sessionId, remoteObj.refs.push);
1557
- return await tempGit.raw(["-c", "credential.helper=", "push", authUrl, "--all"]);
1558
- }
1559
- return await tempGit.raw(["-c", "credential.helper=", "push", remote, "--all"]);
1560
- } catch (err) {
1561
- throw err;
1614
+ if (remoteObj && remoteObj.refs.push) {
1615
+ const authUrl = injectAuthToken(sessionId, remoteObj.refs.push);
1616
+ return await tempGit.raw(["-c", "credential.helper=", "push", authUrl, "--all"]);
1562
1617
  }
1618
+ return await tempGit.raw(["-c", "credential.helper=", "push", remote, "--all"]);
1563
1619
  }
1564
1620
  async function getCommitHistory(sessionId) {
1565
1621
  const { git } = getGitSession(sessionId);
@@ -1577,17 +1633,27 @@ async function getCommitHistory(sessionId) {
1577
1633
  `+refs/heads/*:refs/remotes/${remoteObj.name}/*`
1578
1634
  ]);
1579
1635
  }
1580
- } catch (e) {
1636
+ } catch {
1637
+ }
1638
+ try {
1639
+ const log = await git.log(["--all", "--stat", "--root", "--cc"]);
1640
+ return JSON.parse(JSON.stringify(log));
1641
+ } catch {
1642
+ try {
1643
+ const log = await git.log(["--all", "--stat", "--root"]);
1644
+ return JSON.parse(JSON.stringify(log));
1645
+ } catch {
1646
+ const log = await git.log(["--all", "--name-status"]);
1647
+ return JSON.parse(JSON.stringify(log));
1648
+ }
1581
1649
  }
1582
- const log = await git.log(["--all", "--name-status"]);
1583
- return JSON.parse(JSON.stringify(log));
1584
1650
  }
1585
1651
  async function getDeletedHistory(sessionId) {
1586
1652
  const { git } = getGitSession(sessionId);
1587
1653
  try {
1588
1654
  const log = await git.log(["--all", "--diff-filter=D", "--name-status", "-n", "20"]);
1589
1655
  return JSON.parse(JSON.stringify(log));
1590
- } catch (e) {
1656
+ } catch {
1591
1657
  return { all: [] };
1592
1658
  }
1593
1659
  }
@@ -1681,7 +1747,7 @@ async function setupUniversalWorkspace(sessionId, remoteUrl, branch, baseBranch,
1681
1747
  await setupDefaultGitAttributes(workspacePath);
1682
1748
  let authUrl = remoteUrl;
1683
1749
  if (remoteUrl.includes("github.com")) {
1684
- let cleanUrl = remoteUrl.replace("git@github.com:", "https://github.com/");
1750
+ const cleanUrl = remoteUrl.replace("git@github.com:", "https://github.com/");
1685
1751
  if (cleanUrl.startsWith("https://github.com/")) {
1686
1752
  authUrl = cleanUrl.replace(
1687
1753
  "https://github.com/",
@@ -1744,7 +1810,7 @@ async function getUniversalDiff(workspacePath, filePath) {
1744
1810
  try {
1745
1811
  const diff = await git.raw(["diff", "--", filePath]);
1746
1812
  return diff;
1747
- } catch (e) {
1813
+ } catch {
1748
1814
  return "";
1749
1815
  }
1750
1816
  }
@@ -1759,6 +1825,7 @@ async function commitUniversalWorkspace(workspacePath, message, branch) {
1759
1825
  GIT_ASKPASS: "echo"
1760
1826
  });
1761
1827
  try {
1828
+ await ensureAiWeightsUseLfs(git);
1762
1829
  await git.raw([
1763
1830
  "-c",
1764
1831
  "core.safecrlf=false",
@@ -1781,7 +1848,7 @@ async function commitUniversalWorkspace(workspacePath, message, branch) {
1781
1848
  } catch (err) {
1782
1849
  if (err.message && (err.message.includes("non-fast-forward") || err.message.includes("fetch first") || err.message.includes("branch is behind"))) {
1783
1850
  console.log("Universal push rejected. Auto-pulling with rebase...");
1784
- await git.pull("origin", branch, { "--rebase": null, "--autostash": null });
1851
+ await git.pull("origin", branch, { "--rebase": "false", "--autostash": null, "-X": "ours" });
1785
1852
  await git.raw(["-c", "credential.helper=", "push", "origin", branch]);
1786
1853
  return;
1787
1854
  }
@@ -2064,14 +2131,65 @@ async function scanForRepoFolder(repoName, candidateBaseDirs) {
2064
2131
  async function removeGitLock(sessionId) {
2065
2132
  const { repoPath } = getGitSession(sessionId);
2066
2133
  if (!repoPath) return false;
2067
- const lockPath = require("path").join(repoPath, ".git", "index.lock");
2134
+ const lockPath = path__namespace.join(repoPath, ".git", "index.lock");
2068
2135
  try {
2069
- await require("fs/promises").unlink(lockPath);
2136
+ await fs__namespace.unlink(lockPath);
2070
2137
  return true;
2071
- } catch (e) {
2138
+ } catch {
2072
2139
  return false;
2073
2140
  }
2074
2141
  }
2142
+ async function ensureAiWeightsUseLfs(git) {
2143
+ try {
2144
+ const aiExtensions = [
2145
+ "*.pt",
2146
+ "*.pth",
2147
+ "*.ckpt",
2148
+ "*.safetensors",
2149
+ "*.bin",
2150
+ "*.onnx",
2151
+ "*.h5",
2152
+ "*.tflite",
2153
+ "*.pb",
2154
+ "*.gguf"
2155
+ ];
2156
+ await git.raw(["lfs", "install"]);
2157
+ for (const ext of aiExtensions) {
2158
+ await git.raw(["lfs", "track", ext]);
2159
+ }
2160
+ } catch (e) {
2161
+ console.warn("Failed to configure LFS for AI weights:", e);
2162
+ }
2163
+ }
2164
+ const GITHUB_MODELS_CONFIG = {
2165
+ baseURL: "https://models.inference.ai.azure.com",
2166
+ defaultModel: "gpt-4o-mini",
2167
+ fallbackModel: "meta-llama-3.1-8b-instruct"
2168
+ };
2169
+ const CIPHER_ALGO = "aes-256-cbc";
2170
+ const CIPHER_SECRET = "GitElectron_AI_Core_Salt_2026_SecureKey";
2171
+ const CIPHER_SALT = "salt_gitelectron";
2172
+ const ENCRYPTED_BACKUP_PAYLOAD = "1aa43686251fc27545934564c92ebcd9:acac927d94c7479c0df45b9245f61f47bd4184fca2323e6ce971bc5c74dffaac925caa81788ce81374a05c7072301ee2eac10531be99a850453ff3fd23ea21cd11a8fa95d07d19fe1a8abcdd9a282b06d3e391a621137027c03765f65e977ed432c00e0e9627b2dad77ca708273eff14eac1bbfd01085d4b645a271167bae46edec2a09491b877fba73c3d29dc2e0e2120785347c9189db6bc9892a6018b6dea7ac064b991e9687e2df56e983743985f";
2173
+ function getBackupApiKeys() {
2174
+ try {
2175
+ const [ivHex, dataHex] = ENCRYPTED_BACKUP_PAYLOAD.split(":");
2176
+ if (!ivHex || !dataHex) return [];
2177
+ const key = crypto.scryptSync(CIPHER_SECRET, CIPHER_SALT, 32);
2178
+ const iv = Buffer.from(ivHex, "hex");
2179
+ const decipher = crypto.createDecipheriv(CIPHER_ALGO, key, iv);
2180
+ let decrypted = decipher.update(dataHex, "hex", "utf8");
2181
+ decrypted += decipher.final("utf8");
2182
+ return decrypted.split(",").map((k) => k.trim()).filter((k) => k.length > 0);
2183
+ } catch (err) {
2184
+ console.warn("[AI Setup] Failed to decrypt backup keys in memory:", err);
2185
+ return [];
2186
+ }
2187
+ }
2188
+ function isValidGitHubToken(token) {
2189
+ if (!token || typeof token !== "string") return false;
2190
+ const trimmed = token.trim();
2191
+ return trimmed.startsWith("gho_") || trimmed.startsWith("ghp_") || trimmed.startsWith("github_pat_") || trimmed.length >= 30;
2192
+ }
2075
2193
  const SYSTEM_PROMPT = `You are an autonomous Git Healer Agent for the "GitElectron" app.
2076
2194
  The user has encountered a Git error during a push, pull, or merge operation and you are executing as a background process to silently fix it.
2077
2195
  You will receive the Git Status and the Raw Error Message.
@@ -2090,14 +2208,7 @@ Return the response strictly as a JSON object with this shape, and absolutely no
2090
2208
  "commandsToRun": ["git merge --abort", "git pull --no-edit -X ours"]
2091
2209
  }`;
2092
2210
  async function analyzeGitError(provider, apiKeyInput, errorMsg, gitStatus, repoPath, githubToken) {
2093
- if (!apiKeyInput) {
2094
- if (provider === "gemini") {
2095
- apiKeyInput = "AQ.Ab8RN6LFu9vxQBWCSzE72V4CG2ILxnreqAP4AZQzSmXZ_1vphw";
2096
- } else {
2097
- apiKeyInput = "deepseek-hardcoded-key-if-applicable";
2098
- }
2099
- }
2100
- const keys = apiKeyInput.split(",").map((k) => k.trim()).filter((k) => k);
2211
+ const keys = apiKeyInput ? apiKeyInput.split(",").map((k) => k.trim()).filter((k) => k) : getBackupApiKeys();
2101
2212
  const prompt = `
2102
2213
  GIT STATUS:
2103
2214
  ${JSON.stringify(gitStatus, null, 2)}
@@ -2140,7 +2251,7 @@ ${errorMsg}
2140
2251
  try {
2141
2252
  if (provider === "gemini") {
2142
2253
  const genAI = new generativeAi.GoogleGenerativeAI(apiKey);
2143
- const model = genAI.getGenerativeModel({ model: "gemini-2.5-flash" });
2254
+ const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" });
2144
2255
  const result = await model.generateContent([{ text: SYSTEM_PROMPT }, { text: prompt }]);
2145
2256
  responseText = result.response.text();
2146
2257
  } else if (provider === "deepseek") {
@@ -2245,10 +2356,7 @@ Guidelines:
2245
2356
  - If there are no conflicted files (e.g. just a standard rejection that a pull will fix), omit "resolvedCode".
2246
2357
  - Return ONLY valid JSON. No markdown code block wrappers.`;
2247
2358
  async function analyzePushErrorForAutoSync(provider, apiKeyInput, errorMsg, gitStatus, _repoPath, conflictedFilesContext, githubToken) {
2248
- if (!apiKeyInput && provider === "gemini") {
2249
- apiKeyInput = "AQ.Ab8RN6LFu9vxQBWCSzE72V4CG2ILxnreqAP4AZQzSmXZ_1vphw";
2250
- }
2251
- const keys = apiKeyInput.split(",").map((k) => k.trim()).filter((k) => k);
2359
+ const keys = getBackupApiKeys();
2252
2360
  let prompt = `
2253
2361
  GIT STATUS:
2254
2362
  ${JSON.stringify(gitStatus, null, 2)}
@@ -2269,14 +2377,14 @@ ${content}
2269
2377
  }
2270
2378
  }
2271
2379
  let lastError = null;
2272
- if (githubToken) {
2380
+ if (isValidGitHubToken(githubToken)) {
2273
2381
  try {
2274
2382
  const openai = new OpenAI({
2275
2383
  apiKey: githubToken,
2276
- baseURL: "https://models.inference.ai.azure.com"
2384
+ baseURL: GITHUB_MODELS_CONFIG.baseURL
2277
2385
  });
2278
2386
  const response = await openai.chat.completions.create({
2279
- model: "gpt-4o-mini",
2387
+ model: GITHUB_MODELS_CONFIG.defaultModel,
2280
2388
  messages: [
2281
2389
  { role: "system", content: AUTO_SYNC_SYSTEM_PROMPT },
2282
2390
  { role: "user", content: prompt }
@@ -2298,7 +2406,7 @@ ${content}
2298
2406
  try {
2299
2407
  if (provider === "gemini") {
2300
2408
  const genAI = new generativeAi.GoogleGenerativeAI(apiKey);
2301
- const model = genAI.getGenerativeModel({ model: "gemini-2.5-flash" });
2409
+ const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" });
2302
2410
  const result = await model.generateContent([
2303
2411
  { text: AUTO_SYNC_SYSTEM_PROMPT },
2304
2412
  { text: prompt }
@@ -2330,17 +2438,17 @@ If the diff is empty or invalid, return "Auto-sync: update files".`;
2330
2438
  const prompt = `GIT DIFF:
2331
2439
  ${truncatedDiff}`;
2332
2440
  let keys = "".split(",").map((k) => k.trim()).filter((k) => k);
2333
- if (!keys.length && provider === "gemini") {
2334
- keys = ["AQ.Ab8RN6LFu9vxQBWCSzE72V4CG2ILxnreqAP4AZQzSmXZ_1vphw"];
2441
+ if (!keys.length) {
2442
+ keys = getBackupApiKeys();
2335
2443
  }
2336
- if (githubToken) {
2444
+ if (isValidGitHubToken(githubToken)) {
2337
2445
  try {
2338
2446
  const openai = new OpenAI({
2339
2447
  apiKey: githubToken,
2340
- baseURL: "https://models.inference.ai.azure.com"
2448
+ baseURL: GITHUB_MODELS_CONFIG.baseURL
2341
2449
  });
2342
2450
  const response = await openai.chat.completions.create({
2343
- model: "gpt-4o-mini",
2451
+ model: GITHUB_MODELS_CONFIG.defaultModel,
2344
2452
  messages: [
2345
2453
  { role: "system", content: systemPrompt },
2346
2454
  { role: "user", content: prompt }
@@ -2356,7 +2464,7 @@ ${truncatedDiff}`;
2356
2464
  try {
2357
2465
  if (provider === "gemini") {
2358
2466
  const genAI = new generativeAi.GoogleGenerativeAI(apiKey);
2359
- const model = genAI.getGenerativeModel({ model: "gemini-2.5-flash" });
2467
+ const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" });
2360
2468
  const result = await model.generateContent([{ text: systemPrompt }, { text: prompt }]);
2361
2469
  return result.response.text().trim();
2362
2470
  }
@@ -2457,7 +2565,11 @@ function toggleAutoSync(sessionId, repoPath, enabled, onLog, onErrorApproval, on
2457
2565
  // ignore Office temp lock files (~$README.md, etc.)
2458
2566
  ],
2459
2567
  persistent: true,
2460
- ignoreInitial: true
2568
+ ignoreInitial: true,
2569
+ awaitWriteFinish: {
2570
+ stabilityThreshold: 2e3,
2571
+ pollInterval: 100
2572
+ }
2461
2573
  });
2462
2574
  currentWatcher.on("all", (_event, filePath) => {
2463
2575
  if (!isAutoSyncEnabled || isResolving) return;
@@ -2514,7 +2626,7 @@ async function triggerSyncSequence(sessionId, _repoPath, onLog) {
2514
2626
  return;
2515
2627
  }
2516
2628
  const branch = finalStatus.current || "main";
2517
- onLog("Generating AI commit message...");
2629
+ onLog("🤖 Generating commit summary via GitHub Models...");
2518
2630
  let commitMsg = `Auto-sync: update ${filesToSync.length > 1 ? filesToSync.length + " files" : filesToSync[0]}`;
2519
2631
  try {
2520
2632
  const diffStr = await getFileDiff(sessionId, "", true);
@@ -2532,7 +2644,7 @@ async function triggerSyncSequence(sessionId, _repoPath, onLog) {
2532
2644
  await commitChanges(sessionId, commitMsg);
2533
2645
  onLog(`Pushing to ${branch}...`);
2534
2646
  await pushToRemote(sessionId, "origin", branch);
2535
- onLog(`✅ Successfully pushed auto-sync commit!`);
2647
+ onLog(`✅ Pushed to origin/${branch} [${(/* @__PURE__ */ new Date()).toLocaleTimeString()}]`);
2536
2648
  } catch (error) {
2537
2649
  onLog(`❌ Auto-sync failed: ${error.message}`);
2538
2650
  if (globalOnErrorApproval && !isResolving) {
@@ -2564,7 +2676,7 @@ async function handleAutoSyncError(sessionId, errorMsg, onLog) {
2564
2676
  onLog("Pulling remote changes with rebase...");
2565
2677
  const status2 = await getStatus(sessionId);
2566
2678
  const currentBranch = status2.current || "main";
2567
- await git.pull("origin", currentBranch, { "--rebase": "true", "--autostash": null });
2679
+ await git.pull("origin", currentBranch, { "--rebase": "false", "--autostash": null, "-X": "ours" });
2568
2680
  onLog("Pull successful, attempting push again...");
2569
2681
  await pushToRemote(sessionId, "origin", currentBranch);
2570
2682
  onLog("✅ Error resolved and push resumed!");
@@ -2677,6 +2789,27 @@ function cancelAiFix() {
2677
2789
  isResolving = false;
2678
2790
  }
2679
2791
  const icon = path.join(__dirname, "../../resources/icon.png");
2792
+ if (process.platform === "darwin") {
2793
+ const macPaths = [
2794
+ "/opt/homebrew/bin",
2795
+ "/opt/homebrew/sbin",
2796
+ "/usr/local/bin",
2797
+ "/usr/local/sbin",
2798
+ "/usr/bin",
2799
+ "/bin",
2800
+ "/usr/sbin",
2801
+ "/sbin"
2802
+ ];
2803
+ const currentPath = process.env.PATH || "";
2804
+ const missingPaths = macPaths.filter((p) => !currentPath.includes(p));
2805
+ if (missingPaths.length > 0) {
2806
+ process.env.PATH = `${missingPaths.join(":")}:${currentPath}`;
2807
+ }
2808
+ }
2809
+ if (process.platform === "win32") {
2810
+ electron.app.commandLine.appendSwitch("disable-features", "CalculateNativeWinOcclusion");
2811
+ electron.app.commandLine.appendSwitch("disable-color-correct-rendering");
2812
+ }
2680
2813
  function createWindow(initialPath) {
2681
2814
  const mainWindow = new electron.BrowserWindow({
2682
2815
  width: 1100,
@@ -3031,6 +3164,20 @@ electron.app.whenReady().then(async () => {
3031
3164
  electron.ipcMain.handle("remove-git-lock", async (event) => {
3032
3165
  return await removeGitLock(event.sender.id);
3033
3166
  });
3167
+ electron.ipcMain.handle("clear-app-cache", async (event) => {
3168
+ try {
3169
+ if (electron.session.defaultSession) {
3170
+ await electron.session.defaultSession.clearCache();
3171
+ }
3172
+ try {
3173
+ await removeGitLock(event.sender.id);
3174
+ } catch {
3175
+ }
3176
+ return { success: true };
3177
+ } catch (e) {
3178
+ return { success: false, error: e.message };
3179
+ }
3180
+ });
3034
3181
  electron.ipcMain.handle("check-path-exists", async (_event, targetPath) => {
3035
3182
  if (!targetPath) return false;
3036
3183
  try {
@@ -3116,18 +3263,27 @@ electron.app.whenReady().then(async () => {
3116
3263
  electron.ipcMain.handle("install-update", async () => {
3117
3264
  return new Promise((resolve) => {
3118
3265
  const { spawn } = require("child_process");
3119
- const cmdArgs = [
3120
- "/c",
3121
- "start",
3122
- "cmd.exe",
3123
- "/c",
3124
- "echo Waiting for Git Electron to close so it can be safely updated... & timeout /t 3 /nobreak > nul & echo Installing Update... & npm i -g gitelectron@latest gitquickpush-cli@latest & echo Update Complete! Starting application... & gitelectron open & exit"
3125
- ];
3126
- const child = spawn("cmd.exe", cmdArgs, {
3127
- detached: true,
3128
- stdio: "ignore"
3129
- });
3130
- child.unref();
3266
+ if (process.platform === "win32") {
3267
+ const cmdArgs = [
3268
+ "/c",
3269
+ "start",
3270
+ "cmd.exe",
3271
+ "/c",
3272
+ "echo Waiting for Git Electron to close so it can be safely updated... & timeout /t 3 /nobreak > nul & echo Installing Update... & npm i -g gitelectron@latest gitquickpush-cli@latest & echo Update Complete! Starting application... & gitelectron open & exit"
3273
+ ];
3274
+ const child = spawn("cmd.exe", cmdArgs, {
3275
+ detached: true,
3276
+ stdio: "ignore"
3277
+ });
3278
+ child.unref();
3279
+ } else {
3280
+ const shScript = "sleep 3; npm i -g gitelectron@latest gitquickpush-cli@latest; gitelectron open";
3281
+ const child = spawn("sh", ["-c", shScript], {
3282
+ detached: true,
3283
+ stdio: "ignore"
3284
+ });
3285
+ child.unref();
3286
+ }
3131
3287
  resolve(true);
3132
3288
  setTimeout(() => {
3133
3289
  electron.app.exit(0);
@@ -15,6 +15,7 @@ const api = {
15
15
  initRepo: (path) => electron.ipcRenderer.invoke("init-repo", path),
16
16
  removeGitMetadata: (repoPath) => electron.ipcRenderer.invoke("remove-git-metadata", repoPath),
17
17
  removeGitLock: () => electron.ipcRenderer.invoke("remove-git-lock"),
18
+ clearAppCache: () => electron.ipcRenderer.invoke("clear-app-cache"),
18
19
  getStatus: () => electron.ipcRenderer.invoke("get-status"),
19
20
  stageFiles: (files) => electron.ipcRenderer.invoke("stage-files", files),
20
21
  unstageFiles: (files) => electron.ipcRenderer.invoke("unstage-files", files),