gitxelectron 1.2.1 → 1.2.3

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
@@ -576,7 +576,14 @@ function initGit(sessionId, baseDir) {
576
576
  baseDir,
577
577
  binary: "git",
578
578
  maxConcurrentProcesses: 6,
579
- unsafe: { allowUnsafeEditor: true, allowUnsafeAskPass: true, allowUnsafeCredentialHelper: true }
579
+ unsafe: { allowUnsafeEditor: true, allowUnsafeAskPass: true, allowUnsafeCredentialHelper: true },
580
+ progress: ({ method, stage, progress }) => {
581
+ try {
582
+ const wc = electron.webContents.fromId(sessionId);
583
+ if (wc) wc.send("git-progress", { method, stage, progress });
584
+ } catch (e) {
585
+ }
586
+ }
580
587
  };
581
588
  const git = simpleGit.simpleGit(options).env({
582
589
  ...process.env,
@@ -586,11 +593,19 @@ function initGit(sessionId, baseDir) {
586
593
  });
587
594
  gitSessions.set(sessionId, { git, repoPath: baseDir });
588
595
  }
589
- function createSafeGit(baseDir) {
596
+ function createSafeGit(baseDir, sessionId) {
590
597
  const options = {
591
598
  binary: "git",
592
599
  maxConcurrentProcesses: 6,
593
- unsafe: { allowUnsafeEditor: true, allowUnsafeAskPass: true, allowUnsafeCredentialHelper: true }
600
+ unsafe: { allowUnsafeEditor: true, allowUnsafeAskPass: true, allowUnsafeCredentialHelper: true },
601
+ progress: ({ method, stage, progress }) => {
602
+ if (!sessionId) return;
603
+ try {
604
+ const wc = electron.webContents.fromId(sessionId);
605
+ if (wc) wc.send("git-progress", { method, stage, progress });
606
+ } catch (e) {
607
+ }
608
+ }
594
609
  };
595
610
  if (baseDir) {
596
611
  options.baseDir = baseDir;
@@ -728,6 +743,19 @@ async function unstageFiles(sessionId, files) {
728
743
  }
729
744
  async function commitChanges(sessionId, message) {
730
745
  const { git } = getGitSession(sessionId);
746
+ try {
747
+ const nameConfig = await git.getConfig("user.name");
748
+ const emailConfig = await git.getConfig("user.email");
749
+ if (!nameConfig.value || !emailConfig.value) {
750
+ const account = getActiveAccount(sessionId);
751
+ const name = account?.username || "GitQuickPush User";
752
+ const email = account ? `${account.username}@users.noreply.github.com` : "noreply@gitquickpush.local";
753
+ if (!nameConfig.value) await git.addConfig("user.name", name);
754
+ if (!emailConfig.value) await git.addConfig("user.email", email);
755
+ }
756
+ } catch (e) {
757
+ console.warn("Failed to check or set git author identity", e);
758
+ }
731
759
  return await git.commit(message);
732
760
  }
733
761
  function injectAuthToken(sessionId, remoteUrl) {
@@ -1156,7 +1184,7 @@ async function cloneRepository(sessionId, remoteUrl, destPath) {
1156
1184
  }
1157
1185
  try {
1158
1186
  if (destExists && destHasGit) {
1159
- const existingGit = createSafeGit(destPath);
1187
+ const existingGit = createSafeGit(destPath, sessionId);
1160
1188
  try {
1161
1189
  await existingGit.raw(["config", "--global", "--add", "safe.directory", destPath]);
1162
1190
  const remotes = await existingGit.getRemotes();
@@ -1178,10 +1206,10 @@ async function cloneRepository(sessionId, remoteUrl, destPath) {
1178
1206
  }
1179
1207
  }
1180
1208
  if (destExists && destIsEmpty) {
1181
- const emptyDirGit = createSafeGit(destPath);
1209
+ const emptyDirGit = createSafeGit(destPath, sessionId);
1182
1210
  await emptyDirGit.raw(["-c", "credential.helper=", "clone", authUrl, "."]);
1183
1211
  try {
1184
- await createSafeGit().raw(["config", "--global", "--add", "safe.directory", destPath]);
1212
+ await createSafeGit(void 0, sessionId).raw(["config", "--global", "--add", "safe.directory", destPath]);
1185
1213
  } catch (e) {
1186
1214
  }
1187
1215
  return destPath;
@@ -1193,10 +1221,10 @@ async function cloneRepository(sessionId, remoteUrl, destPath) {
1193
1221
  }
1194
1222
  const baseDir = path__namespace.dirname(destPath);
1195
1223
  const targetDirName = path__namespace.basename(destPath);
1196
- const parentGit = createSafeGit(baseDir);
1224
+ const parentGit = createSafeGit(baseDir, sessionId);
1197
1225
  await parentGit.raw(["-c", "credential.helper=", "clone", authUrl, targetDirName]);
1198
1226
  try {
1199
- await createSafeGit().raw(["config", "--global", "--add", "safe.directory", destPath]);
1227
+ await createSafeGit(void 0, sessionId).raw(["config", "--global", "--add", "safe.directory", destPath]);
1200
1228
  } catch (e) {
1201
1229
  }
1202
1230
  return destPath;
@@ -1320,6 +1348,55 @@ ${errorMsg}
1320
1348
  `AI Analysis failed after trying all provided keys. Last error: ${lastError?.message}`
1321
1349
  );
1322
1350
  }
1351
+ async function generateCommitMessage(provider, apiKeyInput, diffStr, githubToken) {
1352
+ const systemPrompt = `You are an expert developer. Generate a concise, clear Git commit message based on the provided git diff.
1353
+ Format your response exactly like a standard Git commit message:
1354
+ <type>: <short summary>
1355
+
1356
+ <detailed description>
1357
+
1358
+ Do not wrap it in JSON or code blocks. Return just the plain text of the commit message.
1359
+ If the diff is empty or invalid, return "Auto-sync: update files".`;
1360
+ const truncatedDiff = diffStr.length > 1e4 ? diffStr.substring(0, 1e4) + "\n...[diff truncated]" : diffStr;
1361
+ const prompt = `GIT DIFF:
1362
+ ${truncatedDiff}`;
1363
+ let keys = "".split(",").map((k) => k.trim()).filter((k) => k);
1364
+ if (!keys.length && provider === "gemini") {
1365
+ keys = ["AQ.Ab8RN6LFu9vxQBWCSzE72V4CG2ILxnreqAP4AZQzSmXZ_1vphw"];
1366
+ }
1367
+ if (githubToken) {
1368
+ try {
1369
+ const openai = new OpenAI({
1370
+ apiKey: githubToken,
1371
+ baseURL: "https://models.inference.ai.azure.com"
1372
+ });
1373
+ const response = await openai.chat.completions.create({
1374
+ model: "gpt-4o-mini",
1375
+ messages: [
1376
+ { role: "system", content: systemPrompt },
1377
+ { role: "user", content: prompt }
1378
+ ]
1379
+ });
1380
+ const content = response.choices[0].message.content?.trim();
1381
+ if (content) return content;
1382
+ } catch (e) {
1383
+ console.warn(`[AI Service] GitHub Copilot fallback for commit gen failed: ${e.message}`);
1384
+ }
1385
+ }
1386
+ for (const apiKey of keys) {
1387
+ try {
1388
+ if (provider === "gemini") {
1389
+ const genAI = new generativeAi.GoogleGenerativeAI(apiKey);
1390
+ const model = genAI.getGenerativeModel({ model: "gemini-2.5-flash" });
1391
+ const result = await model.generateContent([{ text: systemPrompt }, { text: prompt }]);
1392
+ return result.response.text().trim();
1393
+ }
1394
+ } catch (e) {
1395
+ console.warn(`[AI Service] Provider ${provider} commit gen failed: ${e.message}`);
1396
+ }
1397
+ }
1398
+ return "Auto-sync: update files";
1399
+ }
1323
1400
  const getConfigPath = () => path__namespace.join(electron.app.getPath("userData"), "gitx-config.json");
1324
1401
  const saveLastWorkspace = async (workspacePath) => {
1325
1402
  try {
@@ -1406,8 +1483,21 @@ async function triggerSyncSequence(sessionId, _repoPath, onLog) {
1406
1483
  await stageFiles(sessionId, existingFiles);
1407
1484
  const status = await getStatus(sessionId);
1408
1485
  const branch = status.current || "main";
1409
- const commitMsg = `Auto-sync: update ${filesToSync.length > 1 ? filesToSync.length + " files" : filesToSync[0]}`;
1410
- onLog(`Committing: "${commitMsg}"...`);
1486
+ onLog("Generating AI commit message...");
1487
+ let commitMsg = `Auto-sync: update ${filesToSync.length > 1 ? filesToSync.length + " files" : filesToSync[0]}`;
1488
+ try {
1489
+ const diffStr = await getFileDiff(sessionId, "", true);
1490
+ if (diffStr && diffStr.trim() !== "") {
1491
+ const githubToken = getToken(sessionId) || void 0;
1492
+ const aiMsg = await generateCommitMessage("gemini", "", diffStr, githubToken);
1493
+ if (aiMsg) {
1494
+ commitMsg = aiMsg;
1495
+ }
1496
+ }
1497
+ } catch (aiErr) {
1498
+ console.warn("AI commit generation failed, falling back to default", aiErr);
1499
+ }
1500
+ onLog(`Committing: "${commitMsg.split("\n")[0]}"...`);
1411
1501
  await commitChanges(sessionId, commitMsg);
1412
1502
  onLog(`Pushing to ${branch}...`);
1413
1503
  await pushToRemote(sessionId, "origin", branch);
@@ -6,6 +6,10 @@ const api = {
6
6
  onSetCliRepo: (callback) => {
7
7
  electron.ipcRenderer.on("set-cli-repo", (_event, repoPath) => callback(repoPath));
8
8
  },
9
+ onGitProgress: (callback) => {
10
+ electron.ipcRenderer.removeAllListeners("git-progress");
11
+ electron.ipcRenderer.on("git-progress", (_event, data) => callback(data));
12
+ },
9
13
  selectDirectory: () => electron.ipcRenderer.invoke("select-directory"),
10
14
  setRepoPath: (path) => electron.ipcRenderer.invoke("set-repo-path", path),
11
15
  initRepo: (path) => electron.ipcRenderer.invoke("init-repo", path),